Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve based on open files #24

Merged
merged 1 commit into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
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
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@
"Do not install BSP server if not already present."
],
"description": "Installation behavior for the build server."
},
"bazelbsp.autoExpandTarget": {
"type": "boolean",
"default": "true",
"description": "Find all tests within open files, without waiting for the file's target to be expanded in the Test Explorer."
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/language-tools/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ export class BaseLanguageTools implements LanguageTools {
/**
* No support for individual test cases or test file identification.
* @param document URI of the document to be analyzed for test cases.
* @returns Result always contains isTestFile value of true, and no test cases.
* @returns Result always contains isTestFile value of false, and no test cases.
*/
async getDocumentTestCases(
document: vscode.Uri,
workspaceRoot: string
): Promise<TestFileContents> {
return {
// Do not filter out any files.
isTestFile: true,
isTestFile: false,
// No test case discovery.
testCases: [],
}
Expand Down
9 changes: 9 additions & 0 deletions src/language-tools/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,13 @@ export class LanguageToolManager {
}
return this.baseLanguageTools
}

getLanguageToolsForFile(document: vscode.TextDocument): LanguageTools {
if (document.languageId === 'python') {
return this.pythonLanguageTools
} else if (document.languageId === 'java') {
return this.javaLanguageTools
}
return this.baseLanguageTools
}
}
87 changes: 87 additions & 0 deletions src/test-explorer/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ export class TestResolver implements OnModuleInit, vscode.Disposable {
const directories = new Map<string, vscode.TestItem>()
const buildFileName = getExtensionSetting(SettingName.BUILD_FILE_NAME)
parentTest.children.replace([])
this.store.clearTargetIdentifiers()

result.targets.forEach(target => {
if (!target.capabilities.canTest) return

Expand All @@ -211,6 +213,7 @@ export class TestResolver implements OnModuleInit, vscode.Disposable {
buildFileUri
)
relevantParent.children.add(newTest)
this.store.setTargetIdentifier(target.id, newTest)
})

// If no test items were added, show the getting started message as a test message overlaid on the .bazelproject file.
Expand All @@ -235,6 +238,88 @@ export class TestResolver implements OnModuleInit, vscode.Disposable {
// Replace all children with the newly returned test cases.
this.condenseTestItems(parentTest)
updateDescription(parentTest)

await this.resolveOpenSourceFiles()
}

/**
* Kick off resolution of test items for files that are currently open, and watch for newly opened files.
* This allows test cases to appear without the user having to expand the target's test explorer node.
*/
private async resolveOpenSourceFiles() {
const autoExpandTarget = getExtensionSetting(SettingName.AUTO_EXPAND_TARGET)
// When disabled, tests are discovered as the test explorer tree is expanded.
if (!autoExpandTarget) return

for (const doc of vscode.workspace.textDocuments) {
// Discovery within currently open documents.
await this.expandTargetsForDocument(doc)
}

this.ctx.subscriptions.push(
vscode.workspace.onDidOpenTextDocument(async doc => {
// Discovery within newly opened documents.
await this.expandTargetsForDocument(doc)
})
)
}

/**
* Finds the corresponding target for a given document, if a file's target is not yet known.
* The target's test cases will then be resolved and available in test explorer.
* @param doc for which to find the target.
*/
private async expandTargetsForDocument(doc: vscode.TextDocument) {
if (doc.uri.scheme !== 'file') return

// Avoid checking files that are already known, or not test files.
if (this.store.knownFiles.has(doc.uri.toString())) return
const tools = this.languageToolManager.getLanguageToolsForFile(doc)
const docInfo = await tools.getDocumentTestCases(
doc.uri,
this.repoRoot ?? ''
)
if (!docInfo.isTestFile) return

this.store.knownFiles.add(doc.uri.toString())
const conn = await this.buildServer.getConnection()
const params: bsp.InverseSourcesParams = {
textDocument: {
uri: doc.uri.toString(),
},
}
let result: bsp.InverseSourcesResult | undefined
await vscode.window.withProgress(
{
location: vscode.ProgressLocation.Notification,
title: `Getting target for ${path.basename(doc.uri.fsPath)}.`,
cancellable: true,
},
async (progress, token) => {
result = await conn.sendRequest(
bsp.BuildTargetInverseSources.type,
params,
token
)
}
)

if (!result) {
// TODO(IDE-1203): Add more guidance to update their sync scope.
this.outputChannel.appendLine(`Target not in scope for ${doc.fileName}.`)
return
}

for (const target of result.targets) {
const targetItem = this.store.getTargetIdentifier(target)
if (targetItem) {
await this.resolveHandler(targetItem)
} else {
this.outputChannel.appendLine(
`Couldn't find a matching test item for ${target.uri}.`
)
}
}
}

/**
Expand Down Expand Up @@ -264,6 +349,7 @@ export class TestResolver implements OnModuleInit, vscode.Disposable {

const directories = new Map<string, vscode.TestItem>()
parentTest.children.replace([])
parentTest.canResolveChildren = false
const allDocumentTestItems: vscode.TestItem[] = []
result.items.forEach(target => {
target.sources.forEach(source => {
Expand All @@ -290,6 +376,7 @@ export class TestResolver implements OnModuleInit, vscode.Disposable {
parentTarget,
source
)
this.store.knownFiles.add(source.uri)

relevantParent.children.add(newTest)
allDocumentTestItems.push(newTest)
Expand Down
24 changes: 24 additions & 0 deletions src/test-explorer/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
TEST_CONTROLLER_TOKEN,
} from '../custom-providers'
import {TestCaseInfo} from '../test-info/test-info'
import {BuildTargetIdentifier} from 'src/bsp/bsp'

export class TestCaseStore implements OnModuleInit, vscode.Disposable {
@Inject(EXTENSION_CONTEXT_TOKEN) private readonly ctx: vscode.ExtensionContext
Expand All @@ -15,10 +16,14 @@ export class TestCaseStore implements OnModuleInit, vscode.Disposable {

// Watcher to update a test item's children. Key corresponds to the test item ID.
testItemWatchers: Map<string, vscode.FileSystemWatcher>
knownFiles: Set<string>
private targetIdentifiers: Map<string, vscode.TestItem>

constructor() {
this.testCaseMetadata = new WeakMap<vscode.TestItem, TestCaseInfo>()
this.testItemWatchers = new Map()
this.targetIdentifiers = new Map<string, vscode.TestItem>()
this.knownFiles = new Set<string>()
}

onModuleInit() {
Expand Down Expand Up @@ -59,4 +64,23 @@ export class TestCaseStore implements OnModuleInit, vscode.Disposable {
}
clear(parentTest)
}

setTargetIdentifier(
targetIdentifier: BuildTargetIdentifier,
item: vscode.TestItem
) {
const key = JSON.stringify(targetIdentifier)
this.targetIdentifiers.set(key, item)
}

getTargetIdentifier(
targetIdentifier: BuildTargetIdentifier
): vscode.TestItem | undefined {
const key = JSON.stringify(targetIdentifier)
return this.targetIdentifiers.get(key)
}

clearTargetIdentifiers() {
this.targetIdentifiers.clear()
}
}
2 changes: 1 addition & 1 deletion src/test/suite/language-tools/base.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ suite('Base Language Tools', () => {
vscode.Uri.parse('file:///repo/root/sample/my_test.py'),
'/repo/root/'
)
assert.strictEqual(result.isTestFile, true)
assert.strictEqual(result.isTestFile, false)
assert.strictEqual(result.testCases.length, 0)
})

Expand Down
21 changes: 21 additions & 0 deletions src/test/suite/language-tools/manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,25 @@ suite('Language Tools Manager', () => {
const result = languageTools.getLanguageTools(target)
assert.strictEqual(result.constructor.name, 'BaseLanguageTools')
})

test('get tools for file, python', async () => {
const result = languageTools.getLanguageToolsForFile({
languageId: 'python',
} as any)
assert.strictEqual(result.constructor.name, 'PythonLanguageTools')
})

test('get tools for file, java', async () => {
const result = languageTools.getLanguageToolsForFile({
languageId: 'java',
} as any)
assert.strictEqual(result.constructor.name, 'JavaLanguageTools')
})

test('get tools for file, other', async () => {
const result = languageTools.getLanguageToolsForFile({
languageId: 'other',
} as any)
assert.strictEqual(result.constructor.name, 'BaseLanguageTools')
})
})
Loading
Loading