Skip to content

Commit

Permalink
Fix broken 'asRelativePath' on MacOS
Browse files Browse the repository at this point in the history
  • Loading branch information
fpoli committed Feb 23, 2024
1 parent 2c8b4b2 commit 8f8c704
Showing 1 changed file with 48 additions and 9 deletions.
57 changes: 48 additions & 9 deletions src/test/extension.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,33 @@ import * as config from "../config";
import * as state from "../state";
import * as extension from "../extension"

/**
* Get the path of the workspace.
*
* @returns The path of the workspace.
*/
function workspacePath(): string {
assert.ok(vscode.workspace.workspaceFolders?.length);
return vscode.workspace.workspaceFolders[0].uri.fsPath;
}

/**
* Convert a URI to a relative path to the workspace.
* This is needed to work around a bug of `asRelativePath` on MacOS.
*
* @param uri The URI to convert.
* @returns The computed relative path in the workspace.
*/
function asRelativeWorkspacePath(uri: vscode.Uri): string {
const relative = vscode.workspace.asRelativePath(uri);
// For some reason, `asRelativePath` returns an absolute path on MacOS.
// So, we compute it in a different way.
if (path.isAbsolute(relative)) {
return path.relative(workspacePath(), relative);
}
return relative;
}

/**
* Open a file in the IDE
* @param filePath The file to open.
Expand All @@ -31,7 +53,7 @@ function openFile(filePath: string): Promise<vscode.TextDocument> {
}

/**
* Evaluate tje filter used in the `.rs.json` expected diagnostics.
* Evaluate one of the filters contained in the `.rs.json` expected diagnostics.
* @param filter The filter dictionary.
* @param name The name of the filter.
* @returns True if the filter is fully satisfied, otherwise false.
Expand All @@ -56,22 +78,25 @@ function evaluateFilter(filter: [string: string], name: string): boolean {
return true;
}

// JSON-like types used to represent diagnostics
// JSON-like types used to normalize the diagnostics
type Position = {
line: number,
character: number
}

type Range = {
start: Position,
end: Position
}

type RelatedInformation = {
location: {
uri: string,
range: Range,
},
message: string
}

type Diagnostic = {
// This path is relative to VSCode's workspace
uri: string,
Expand All @@ -93,19 +118,26 @@ function rangeToPlainObject(range: vscode.Range): Range {
}
};
}

/**
* Normalize a diagnostic, converting it to a plain object.
*
* @param uri The URI of the file containing the diagnostic.
* @param diagnostic The diagnostic to convert.
* @returns The normalized diagnostic.
*/
function diagnosticToPlainObject(uri: vscode.Uri, diagnostic: vscode.Diagnostic): Diagnostic {
const plainDiagnostic: Diagnostic = {
uri: vscode.workspace.asRelativePath(uri),
uri: asRelativeWorkspacePath(uri),
range: rangeToPlainObject(diagnostic.range),
severity: diagnostic.severity,
message: diagnostic.message,
};
if (diagnostic.relatedInformation) {
plainDiagnostic.relatedInformation = diagnostic.relatedInformation.map((relatedInfo) => {
const uri = vscode.workspace.asRelativePath(relatedInfo.location.uri);
return {
location: {
uri: uri,
uri: asRelativeWorkspacePath(relatedInfo.location.uri),
range: rangeToPlainObject(relatedInfo.location.range)
},
message: relatedInfo.message,
Expand All @@ -115,7 +147,7 @@ function diagnosticToPlainObject(uri: vscode.Uri, diagnostic: vscode.Diagnostic)
return plainDiagnostic;
}

// Prepare the workspace
// Constants used in the tests
const PROJECT_ROOT = path.join(__dirname, "..", "..");
const SCENARIOS_ROOT = path.join(PROJECT_ROOT, "src", "test", "scenarios");
const SCENARIO = process.env.SCENARIO;
Expand Down Expand Up @@ -156,27 +188,32 @@ describe("Extension", () => {
})

it(`scenario ${SCENARIO} can update Prusti`, async () => {
// tests are run serially, so nothing will run & break while we're updating
// Tests are run serially, so nothing will run & break while we're updating
await openFile(path.join(workspacePath(), "programs", "assert_true.rs"));
await vscode.commands.executeCommand("prusti-assistant.update");
});

// Test every Rust program in the workspace
// Generate a test for every Rust program with expected diagnostics in the test suite.
const programs: Array<string> = [SCENARIO_PATH, SHARED_SCENARIO_PATH].flatMap(cwd =>
glob.sync("**/*.rs.json", { cwd: cwd }).map(filePath => filePath.replace(/\.json$/, ""))
);
console.log(`Creating tests for ${programs.length} programs: ${programs}`);
assert.ok(programs.length >= 3, `There are not enough programs to test (${programs.length})`);
programs.forEach(program => {
it(`scenario ${SCENARIO} reports expected diagnostics on ${program}`, async () => {
// Verify the program
const programPath = path.join(workspacePath(), program);
await openFile(programPath);
await vscode.commands.executeCommand("prusti-assistant.clear-diagnostics");
await vscode.commands.executeCommand("prusti-assistant.verify");

// Collect and normalize the diagnostics
const plainDiagnostics = vscode.languages.getDiagnostics().flatMap(pair => {
const [uri, diagnostics] = pair;
return diagnostics.map(diagnostic => diagnosticToPlainObject(uri, diagnostic));
});

// Load the expected diagnostics. A single JSON file can contain multiple alternatives.
const expectedData = await fs.readFile(programPath + ".json", "utf-8");
type MultiDiagnostics = [
{ filter?: [string: string], diagnostics: Diagnostic[] }
Expand All @@ -190,7 +227,8 @@ describe("Extension", () => {
} else {
expectedMultiDiagnostics = expected as MultiDiagnostics;
}
// Different Prusti versions or OSs migh report slightly different diagnostics.

// Select the expected diagnostics to be used for the current environment
let expectedDiagnostics = expectedMultiDiagnostics.find((alternative, index) => {
if (!alternative.filter) {
console.log(
Expand All @@ -210,6 +248,7 @@ describe("Extension", () => {
};
}

// Compare the actual with the expected diagnostics
expect(plainDiagnostics).to.deep.equal(expectedDiagnostics.diagnostics);
});
});
Expand Down

0 comments on commit 8f8c704

Please sign in to comment.