Skip to content

Commit

Permalink
fix(coverage): Expand external local repo in coverage report (#418)
Browse files Browse the repository at this point in the history
Rules under repo local_repository() are external repo but symlinked
to a local path, usually in the same workspace.

No matter if the local path is in the same workspace, the users should
usually want to check the local path specified in local_repository rules
instead of the path in bazel output (`<bazel workspace>/bazel-<repo>/external/...`)
in the file explorer.

Works towards #416. Improves #362.
  • Loading branch information
cwahbong authored Oct 16, 2024
1 parent b4a8b0c commit c115a86
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 9 deletions.
16 changes: 8 additions & 8 deletions src/bazel/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,12 @@ async function onTaskProcessEnd(event: vscode.TaskProcessEndEvent) {
if (taskDefinition.command === "coverage" && rawExitCode === 0) {
// Find the coverage file and load it.
const workspaceInfo = await getWorkspaceInfoFromTask(task.scope);
const outputPath = await new BazelInfo(
const bazelInfo = new BazelInfo(
getDefaultBazelExecutablePath(),
workspaceInfo.bazelWorkspacePath,
).getOne("output_path");
);
const outputPath = await bazelInfo.getOne("output_path");
const executionRoot = await bazelInfo.getOne("execution_root");

// Build a description string which will be displayed as part of the test run.
const execution = task.execution as vscode.ShellExecution;
Expand All @@ -183,12 +185,10 @@ async function onTaskProcessEnd(event: vscode.TaskProcessEndEvent) {
"the instrumentation filters are set correctly.",
);
} else {
// Show the coverage date
await showLcovCoverage(
description,
workspaceInfo.bazelWorkspacePath,
covFileStr,
);
// The `bazel coverage` runs the build/test/coverage in sandboxes with
// the similar/same layout of execution root, thus using it as the base
// for mapping the source files.
await showLcovCoverage(description, executionRoot, covFileStr);
}
} catch (e: any) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
Expand Down
4 changes: 4 additions & 0 deletions src/test-explorer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ export function activateTesting(): vscode.Disposable[] {

/**
* Display coverage information from a `.lcov` file.
*
* @param description The heading message for test (coverage) run.
* @param baseFolder The source file entries are relative paths to baseFolder.
* @param lcov The lcov report data in string.
*/
export async function showLcovCoverage(
description: string,
Expand Down
32 changes: 31 additions & 1 deletion src/test-explorer/lcov_parser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as vscode from "vscode";
import * as fs from "fs/promises";
import * as path from "path";
import * as child_process from "child_process";
import * as which from "which";
Expand Down Expand Up @@ -128,6 +129,32 @@ async function demangleNameUsingFilter(
return unmangled;
}

async function resolveSourceFilePath(
baseFolder: string,
sfPath: string,
): Promise<string> {
// Ignore and keep the not existing paths for matching in later
// phases.
const resolvedSfPath = path.resolve(baseFolder, sfPath);
// Path could be in pattern `external/<local_repository name>/...`,
// Try resolve the symlink for <exec root>/external/<local_repository name>,
// so that the SF path could be patched into `<real>/<path>/<to>/<repo>/...`.
const externalRepoMatch = sfPath.match(/^external\/([^/]+)(\/.*)/);
if (externalRepoMatch) {
const repoName = externalRepoMatch[1];
const rest = externalRepoMatch[2];
try {
const repoPath = await fs.realpath(`${baseFolder}/external/${repoName}`);
const realSourcePath = `${repoPath}${rest}`;
await fs.stat(realSourcePath);
return realSourcePath;
} catch {
// Ignore invalid paths and fallback to original resolved one.
}
}
return resolvedSfPath;
}

/**
* Coverage data from a Bazel run.
*
Expand All @@ -154,6 +181,9 @@ export class BazelFileCoverage extends vscode.FileCoverage {

/**
* Parses the LCOV coverage info into VS Code's representation
*
* @param baseFolder The source file entries are relative paths to baseFolder.
* @param lcov The lcov report data in string.
*/
export async function parseLcov(
baseFolder: string,
Expand Down Expand Up @@ -203,7 +233,7 @@ export async function parseLcov(
if (info !== undefined) {
throw new Error(`Duplicated SF entry`);
}
const filename = path.resolve(baseFolder, value);
const filename = await resolveSourceFilePath(baseFolder, value);
if (!infosByFile.has(filename)) {
infosByFile.set(filename, new FileCoverageInfo());
}
Expand Down

0 comments on commit c115a86

Please sign in to comment.