Skip to content

Commit

Permalink
Merge remote branch 'origin/master' into edge
Browse files Browse the repository at this point in the history
  • Loading branch information
automatic-merge committed Apr 22, 2024
2 parents 42aa322 + a37a7f7 commit ac5e73e
Show file tree
Hide file tree
Showing 15 changed files with 148 additions and 56 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ endif

vscode-test:
# Run the VS Code integration testsuite.
MOCHA_ALS_UPDATE=$(MOCHA_ALS_UPDATE) cd integration/vscode/ada; LD_LIBRARY_PATH= npm run test
cd integration/vscode/ada; MOCHA_ALS_UPDATE=$(MOCHA_ALS_UPDATE) LD_LIBRARY_PATH= python vscode-test-win-workaround.py npm run test

vscode-package:
cd integration/vscode/ada; LD_LIBRARY_PATH= $(VSCE) package
Expand Down
1 change: 0 additions & 1 deletion integration/vscode/ada/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
/*.vsix
/out/
/.vscode-test/
**/obj/

# Locations of ALS executable in VS Code extension. VS Code is supported on arm,
# arm64 and x64 so we only consider those architectures.
Expand Down
1 change: 1 addition & 0 deletions integration/vscode/ada/.vscodeignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ tsconfig.json
**/*.ts
**/*.ts.map
xfail.yaml
vscode-test-win-workaround.py
97 changes: 62 additions & 35 deletions integration/vscode/ada/src/gnattest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as vscode from 'vscode';
import { TestItem } from 'vscode';
import { CancellationToken } from 'vscode-languageclient';
import { adaExtState } from './extension';
import { exe, getObjectDir } from './helpers';
import { escapeRegExp, exe, getObjectDir } from './helpers';

export let controller: vscode.TestController;
export let testRunProfile: vscode.TestRunProfile;
Expand Down Expand Up @@ -524,17 +524,20 @@ async function handleRunRequestedTests(request: vscode.TestRunRequest, token?: C
return !excludedLeafTests?.includes(t);
});

await buildTestDriver(run);
/**
* Mark tests as queued
*/
testsToRun.forEach((t) => run.enqueued(t));

/**
* Build the test driver
*/
await buildTestDriverAndReportErrors(run, testsToRun);

if (token?.isCancellationRequested) {
throw new vscode.CancellationError();
}

/**
* Mark tests as queued for execution
*/
testsToRun.forEach((t) => run.enqueued(t));

/**
* Invoke the test driver for each test
*/
Expand Down Expand Up @@ -563,6 +566,30 @@ async function handleRunRequestedTests(request: vscode.TestRunRequest, token?: C
}
}

/**
* Build the test driver and report build failure as errors on the tests
* requested for execution.
*
* @param run - the TestRun hosting the execution
* @param testsToRun - the tests requested for execution - build failure will be
* reported on those tests.
*/
async function buildTestDriverAndReportErrors(run: vscode.TestRun, testsToRun: vscode.TestItem[]) {
try {
await buildTestDriver(run);
} catch (error) {
/**
* In case of failure, report all tests as errored.
*/
const md = getBuildErrorMessage();
for (const test of testsToRun) {
run.errored(test, new vscode.TestMessage(md));
}
run.end();
throw error;
}
}

/**
* VS Code terminals expect `\r\n` as a line separator, so this function is
* a wrapper to prepare the given output to use that line separator and
Expand All @@ -584,9 +611,19 @@ async function handleRunAll(request: vscode.TestRunRequest, token?: Cancellation
const run = controller.createTestRun(request, undefined, false);
try {
/**
* First we need to build the test driver project.
* Collect all tests, i.e. all leafs of the TestItem tree.
*/
await buildTestDriver(run);
const allTests: TestItem[] = collectLeafsFromCollection(controller.items, token);

/**
* Mark tests as queued
*/
allTests.forEach((t) => run.enqueued(t));

/**
* Build the test driver
*/
await buildTestDriverAndReportErrors(run, allTests);

if (token?.isCancellationRequested) {
throw new vscode.CancellationError();
Expand All @@ -603,11 +640,6 @@ async function handleRunAll(request: vscode.TestRunRequest, token?: Cancellation
throw new vscode.CancellationError();
}

/**
* Now let's collect all tests, i.e. all leafs of the TestItem tree.
*/
const allTests: TestItem[] = collectLeafsFromCollection(controller.items, token);

/**
* Mark all tests as started.
*/
Expand Down Expand Up @@ -645,6 +677,21 @@ async function handleRunAll(request: vscode.TestRunRequest, token?: Cancellation
}
}

/**
* Failures to build the test driver are reported as test errors to make them
* clearly visible.
*
* @returns the message to be used as a test failure when the test driver fails
* to build.
*/
function getBuildErrorMessage() {
const md = new vscode.MarkdownString(
'Failed to build the test driver, [view output](command:testing.showMostRecentOutput)'
);
md.isTrusted = true;
return md;
}

/**
* Invoke gprbuild on the test driver project and pipe the output into the given
* TestRun object.
Expand All @@ -658,21 +705,12 @@ async function buildTestDriver(run: vscode.TestRun) {
*/
const driverPrjPath = await getGnatTestDriverProjectPath();
run.appendOutput(`Building the test harness project\r\n`);
const gprbuild = logAndRun(run, ['gprbuild', '-P', driverPrjPath]);
const gprbuild = logAndRun(run, ['gprbuild', '-P', driverPrjPath, '-cargs:ada', '-gnatef']);

prepareAndAppendOutput(run, gprbuild.stdout.toLocaleString());
prepareAndAppendOutput(run, gprbuild.stderr.toLocaleString());

if (gprbuild.status !== 0) {
/**
* Failed to build the test driver. The output of gprbuild is
* usually pretty explicit so no need to add an error message.
*
* No need to show an error tooltip because the exception raised below
* already causes that.
*/
run.end();

throw Error('Error while building the test driver');
}
}
Expand Down Expand Up @@ -798,17 +836,6 @@ export function collectLeafItems(item: TestItem, token?: CancellationToken): vsc
}
}

/**
*
* @param text - a string possibly containing special RegExp characters.
* @returns a string where all RegExp special characters have been escaped. This
* can be useful when searching for an exact string which may contain special
* characters.
*/
function escapeRegExp(text: string) {
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}

/**
*
* Run a command line as a child process and pipe the output into the TestRun
Expand Down
11 changes: 11 additions & 0 deletions integration/vscode/ada/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,3 +425,14 @@ export function getSymbols(

return allSymbols;
}

/**
*
* @param text - a string possibly containing special RegExp characters.
* @returns a string where all RegExp special characters have been escaped. This
* can be useful when searching for an exact string which may contain special
* characters.
*/
export function escapeRegExp(text: string) {
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}
12 changes: 10 additions & 2 deletions integration/vscode/ada/test/suite/general/tasks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,16 @@ ada: Build and run main - src/test.adb - kind: buildAndRunMain`.trim();
const execStatus: number | undefined = await runTaskAndGetResult(task);
assert.equal(execStatus, 0);
} finally {
// Reset the 'ada.projectFile' setting.
await vscode.workspace.getConfiguration().update('ada.projectFile', initialProjectFile);
// Reset the 'ada.projectFile' setting. If the previous value was
// empty, update to 'undefined' so that the setting gets removed.
// That's because the default value of that setting is the empty
// string.
await vscode.workspace
.getConfiguration()
.update(
'ada.projectFile',
initialProjectFile === '' ? undefined : initialProjectFile
);
}
});

Expand Down
15 changes: 7 additions & 8 deletions integration/vscode/ada/test/suite/gnattest/gnattest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
runHandler,
} from '../../../src/gnattest';
import { activate } from '../utils';
import { escapeRegExp } from '../../../src/helpers';

suite('GNATtest Integration Tests', function () {
this.beforeAll(async () => {
Expand Down Expand Up @@ -329,8 +330,7 @@ bar
*/
const buildOutput = `
Building the test harness project
$ "gprbuild" "-P" "${await getGnatTestDriverProjectPath()}"
`.trimStart();
$ "gprbuild" "-P" "${await getGnatTestDriverProjectPath()}"`.trimStart();
const runOutput = `
Running the test driver
$ "${await getGnatTestDriverExecPath()}" "--passed-tests=show"
Expand All @@ -340,19 +340,18 @@ speed2.ads:12:4: info: corresponding test PASSED
speed2.ads:16:4: info: corresponding test PASSED
speed1.ads:12:4: inherited at speed2.ads:20:4: info: corresponding test PASSED
speed2.ads:10:4: error: corresponding test FAILED: Test not implemented. (speed2-auto_controller_test_data-auto_controller_tests.adb:46)
6 tests run: 5 passed; 1 failed; 0 crashed.
`.trimStart();
assert.ok(outputs.includes(buildOutput));
assert.ok(outputs.includes(runOutput));
6 tests run: 5 passed; 1 failed; 0 crashed.`.trimStart();
assert.match(outputs, RegExp(escapeRegExp(buildOutput)));
assert.match(outputs, RegExp(escapeRegExp(runOutput)));

/**
* Check that calling the run handler with an empty include array
* also yields an execution of all tests.
*/
outputs = '';
await runHandler({ include: [], exclude: undefined, profile: undefined }, undefined);
assert.ok(outputs.includes(buildOutput));
assert.ok(outputs.includes(runOutput));
assert.match(outputs, RegExp(escapeRegExp(buildOutput)));
assert.match(outputs, RegExp(escapeRegExp(runOutput)));
} finally {
/**
* Reset the controller object on which we set a spy
Expand Down
17 changes: 8 additions & 9 deletions integration/vscode/ada/test/suite/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import assert from 'assert';
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { Glob, GlobOptionsWithFileTypesUnset } from 'glob';
import Mocha, { MochaOptions } from 'mocha';
import path from 'path';
import { env } from 'process';
import * as vscode from 'vscode';

/**
Expand Down Expand Up @@ -54,9 +50,12 @@ export function update(): boolean {
*/
export async function activate(): Promise<void> {
const ext = vscode.extensions.getExtension('AdaCore.ada');
if (ext !== undefined) {
if (!ext.isActive) {
await ext.activate();
}
}
assert(ext);
/**
* Previously this code returned when ext.isActive was true. This is not
* enough because it doesn't indicate if any errors occured during
* activation. Instead, always awaiting the result of the activate() method
* does report activation errors as a promise rejection.
*/
await ext.activate();
}
5 changes: 5 additions & 0 deletions integration/vscode/ada/test/workspaces/general/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Object dir of the project
/obj/

# Generated when testing gnatsas tasks
/report.sarif
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# This project has no Object_Dir defined, so we have to git-ignore specific
# extensions and files
/*.o
/*.ali
/*.stdout
/*.stderr
/b__*.ad?
/*.bexch
/main1exec
2 changes: 2 additions & 0 deletions integration/vscode/ada/test/workspaces/gnattest/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Object dir of the project
/obj/
23 changes: 23 additions & 0 deletions integration/vscode/ada/vscode-test-win-workaround.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""
The vscode-test tool has a bug on Windows. There is a workaround of spawning the node
process with a CWD that has a lowercase drive letter. This script implements that
workaround.
See https://github.com/microsoft/vscode-test/issues/136#issuecomment-2049714028
"""

import os
from pathlib import Path
import subprocess
import sys


cwd = Path().absolute()

windows = os.environ.get("OS") == "Windows_NT"
if windows:
# Convert the path to a lowercase drive letter
cwd = Path(f"{cwd.drive.lower()}\\", *cwd.parts[1:])

# Use shell=True on Windows to resolve the first argument based on PATH
exit(subprocess.call(sys.argv[1:], cwd=cwd, shell=windows))
3 changes: 3 additions & 0 deletions source/ada/lsp-ada_document_symbol.adb
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,9 @@ package body LSP.Ada_Document_Symbol is

when Libadalang.Common.Ada_Basic_Decl =>
for Name of Node.As_Basic_Decl.P_Defining_Names loop

exit when Name.Is_Null;

Append_Name
(Name => Name,
Text => VSS.Strings.To_Virtual_String (Name.Text),
Expand Down
4 changes: 4 additions & 0 deletions source/ada/lsp-ada_highlighters.adb
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,10 @@ package body LSP.Ada_Highlighters is

Token : Libadalang.Common.Token_Reference := From_Token;
begin
if Libadalang.Common.Is_Trivia (Token) then
Token := Libadalang.Common.Next (Token, Exclude_Trivia => True);
end if;

-- Scan over all tokens and find a corresponding value in Holder
while Token < To_Token loop

Expand Down

0 comments on commit ac5e73e

Please sign in to comment.