Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 30 additions & 6 deletions src/commands/testExplorerCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,32 +53,56 @@ export async function refreshExplorer(): Promise<void> {
}

/**
* Ensure the URI string ends with '/' so that startsWith comparisons
* are path-segment-aware (e.g. ".../app/" won't prefix-match ".../app2/").
*/
function ensureTrailingSeparator(uriString: string): string {
return uriString.endsWith('/') ? uriString : uriString + '/';
}/**
* Refresh only the project subtree that matches the given classpath-change URI.
* Falls back to a full (incremental) refresh if no matching project is found.
* If the URI is an ancestor of known test projects, refreshes all matching children.
* If the URI doesn't correspond to any known test project, the refresh is skipped
* to avoid unnecessary full reloads (e.g. for non-test projects like Gradle's buildSrc).
*/
export async function refreshProject(classpathUri: Uri): Promise<void> {
sendInfo('', { name: 'refreshProject' });
const uriString: string = classpathUri.toString();
const uriString: string = ensureTrailingSeparator(classpathUri.toString());

// Find the project root with the longest matching URI prefix (most specific match)
// Find the project root with the longest matching URI prefix (most specific match),
// or find a project whose URI is a child of the classpath URI (e.g. workspace root changed).
// All comparisons use separator-terminated URIs to avoid false positives
// (e.g. "file:///ws/app" must not match "file:///ws/app2").
let matchedProject: TestItem | undefined;
let matchedUriLength: number = 0;
let childProjectMatched: boolean = false;
testController?.items.forEach((root: TestItem) => {
if (root.uri) {
const rootUriString: string = root.uri.toString();
const rootUriString: string = ensureTrailingSeparator(root.uri.toString());
if (uriString.startsWith(rootUriString) && rootUriString.length > matchedUriLength) {
matchedProject = root;
matchedUriLength = rootUriString.length;
} else if (rootUriString.startsWith(uriString)) {
// The classpath URI is an ancestor of this project (e.g. workspace root)
childProjectMatched = true;
}
}
});

if (matchedProject) {
// Re-resolve only the matched project's children
await loadChildren(matchedProject);
} else if (childProjectMatched) {
// The classpath URI is an ancestor containing test projects – refresh all children
const loadPromises: Promise<void>[] = [];
testController?.items.forEach((root: TestItem) => {
if (root.uri && ensureTrailingSeparator(root.uri.toString()).startsWith(uriString)) {
loadPromises.push(loadChildren(root));
}
});
await Promise.all(loadPromises);
} else {
// No matching project found – do incremental full refresh
await loadJavaProjects();
// URI doesn't match any known test project – skip to avoid unnecessary full refresh
return;
}
await showTestItemsInCurrentFile();
}