[Mlir-commits] [mlir] [mlir-vscode] Added LIT test discovery functionality (PR #111070)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Thu Oct 3 15:38:43 PDT 2024
https://github.com/jjalowie created https://github.com/llvm/llvm-project/pull/111070
(Work in progress)
This is a draft of adding LIT test discovery in the VSCode MLIR extension.
The implemented functionality adds a list of LIT tests in the Testing Tab in VSCode which looks like this:
![image](https://github.com/user-attachments/assets/61f72759-6de3-4607-8e4a-858ad732305f)
Things I want to implement:
- test discovery by a configurable path to the folder with tests through `.vscode/settings.json`
- passing arguments to lit.py which are configure through .vscode/settings.json
- running a single as well as a subset of LIT tests through the "Run tests" button
- in case of LIT test failures the failure output should be stored and accessible for the programmer
- (optionally) debugging LIT tests, i.e. running the mlir-opt command of the LIT tests in a configurable debugger (lldb/gdb)
- (not sure if this is feasible) listing all test chunks when using the `--split-input-file` option and enabling triggering a single chunk within the LIT test instead of running the whole file
>From d3cf13a1dfae66f7a2730e033d473288f091eea4 Mon Sep 17 00:00:00 2001
From: Kuba Jalowiec <jakub.jalowiec at intel.com>
Date: Thu, 3 Oct 2024 20:38:58 +0000
Subject: [PATCH] [mlir-vscode] Added LIT test discovery functionality
---
mlir/utils/vscode/package.json | 27 ++++-
mlir/utils/vscode/src/LIT/lit.ts | 178 +++++++++++++++++++++++++++++
mlir/utils/vscode/src/extension.ts | 8 +-
3 files changed, 210 insertions(+), 3 deletions(-)
create mode 100644 mlir/utils/vscode/src/LIT/lit.ts
diff --git a/mlir/utils/vscode/package.json b/mlir/utils/vscode/package.json
index 6d0f6f5c88adb8..4215ca2b681811 100644
--- a/mlir/utils/vscode/package.json
+++ b/mlir/utils/vscode/package.json
@@ -2,7 +2,7 @@
"name": "vscode-mlir",
"displayName": "MLIR",
"description": "MLIR Language Extension",
- "version": "0.0.12",
+ "version": "0.0.13",
"publisher": "llvm-vs-code-extensions",
"homepage": "https://mlir.llvm.org/",
"icon": "icon.png",
@@ -25,7 +25,8 @@
"onCustomEditor:mlir.bytecode",
"onLanguage:mlir",
"onLanguage:pdll",
- "onLanguage:tablegen"
+ "onLanguage:tablegen",
+ "onTestingStart"
],
"main": "./out/extension",
"scripts": {
@@ -106,6 +107,14 @@
"configuration": "./tablegen-language-configuration.json"
}
],
+ "testing": {
+ "controllers": [
+ {
+ "id": "litTestController",
+ "label": "LIT Tests"
+ }
+ ]
+ },
"grammars": [
{
"language": "mlir",
@@ -150,6 +159,16 @@
"type": "object",
"title": "MLIR",
"properties": {
+ "lit.lit_path": {
+ "type": "string",
+ "default": "lit.py",
+ "description": "Path to the lit.py script."
+ },
+ "lit.test_root_folder": {
+ "type": "string",
+ "default": ".",
+ "description": "Path to the folder containing lit tests."
+ },
"mlir.server_path": {
"scope": "resource",
"type": "string",
@@ -208,6 +227,10 @@
}
},
"commands": [
+ {
+ "command": "lit.reconfigure",
+ "title": "Reconfigure LIT Test Settings"
+ },
{
"command": "mlir.restart",
"title": "mlir: Restart language server"
diff --git a/mlir/utils/vscode/src/LIT/lit.ts b/mlir/utils/vscode/src/LIT/lit.ts
new file mode 100644
index 00000000000000..e3411da77491b9
--- /dev/null
+++ b/mlir/utils/vscode/src/LIT/lit.ts
@@ -0,0 +1,178 @@
+import * as vscode from 'vscode';
+import * as fs from 'fs';
+import * as path from 'path';
+
+export class LitTestProvider implements vscode.Disposable {
+ private controller: vscode.TestController;
+ private testItemRoot: vscode.TestItem;
+
+ constructor(context: vscode.ExtensionContext) {
+ // Create the TestController and root test item
+ this.controller = vscode.tests.createTestController('litTestController', 'LIT Tests');
+ this.testItemRoot = this.controller.createTestItem('litTestsRoot', 'LIT Tests');
+ this.controller.items.add(this.testItemRoot);
+ context.subscriptions.push(this.controller);
+
+ // Discover tests initially on extension activation
+ this.discoverLitTests();
+
+ // Set up file open listener for MLIR files
+ vscode.workspace.onDidOpenTextDocument(document => {
+ if (document.uri.fsPath.endsWith('.mlir')) {
+ console.log(`MLIR file opened: ${document.uri.fsPath}`);
+ this.discoverLitTests();
+ }
+ });
+
+ // Set up run profile for running tests
+ this.controller.createRunProfile('Run Tests', vscode.TestRunProfileKind.Run, async (request, token) => {
+ const run = this.controller.createTestRun(request);
+ for (const test of request.include ?? []) {
+ await this.runTest(run, test, token);
+ }
+ run.end();
+ });
+ }
+
+ // Function to prompt the user to re-enter the LIT tool path and test folder path
+ public async reconfigureLitSettings() {
+ const config = vscode.workspace.getConfiguration('lit');
+
+ // Prompt for the lit tool path and update the configuration
+ const litToolPath = await this.promptForPath('Please re-enter the path to the lit tool');
+ if (litToolPath) {
+ await config.update('lit_path', litToolPath, vscode.ConfigurationTarget.Workspace);
+ }
+
+ // Prompt for the test folder path and update the configuration
+ const testFolderPath = await this.promptForPath('Please re-enter the path to the folder containing LIT tests');
+ if (testFolderPath) {
+ await config.update('test_root_folder', testFolderPath, vscode.ConfigurationTarget.Workspace);
+ }
+
+ // Rediscover tests after reconfiguration
+ this.discoverLitTests();
+ }
+
+ // Function to discover LIT tests and display them in the test explorer
+ private async discoverLitTests() {
+ const config = vscode.workspace.getConfiguration('lit');
+
+ // Get the lit tool path and test folder from the config
+ let litToolPath = config.get<string>('lit_path');
+ let testFolderPath = config.get<string>('test_root_folder');
+
+ // If the lit tool path or test folder is not set, prompt the user to enter them
+ if (!litToolPath) {
+ litToolPath = await this.promptForPath('Please enter the path to the lit tool');
+ if (litToolPath) {
+ await config.update('lit_path', litToolPath, vscode.ConfigurationTarget.Workspace);
+ }
+ }
+
+ if (!testFolderPath) {
+ testFolderPath = await this.promptForPath('Please enter the path to the folder containing LIT tests');
+ if (testFolderPath) {
+ await config.update('test_root_folder', testFolderPath, vscode.ConfigurationTarget.Workspace);
+ }
+ }
+
+ // Ensure both values are now set before proceeding
+ if (!litToolPath || !testFolderPath) {
+ vscode.window.showErrorMessage('LIT tool path or test folder path not set. Test discovery cannot proceed.');
+ return;
+ }
+
+ // Ensure the test folder path is absolute (relative to workspace)
+ const absoluteTestFolderPath = path.isAbsolute(testFolderPath)
+ ? testFolderPath
+ : path.join(vscode.workspace.workspaceFolders?.[0].uri.fsPath || '', testFolderPath);
+
+ if (!fs.existsSync(absoluteTestFolderPath)) {
+ vscode.window.showErrorMessage(`Test folder not found: ${absoluteTestFolderPath}`);
+ return;
+ }
+
+ // Clear previous test items before discovering new ones
+ this.testItemRoot.children.replace([]); // Use replace([]) to clear the test items
+
+ // Recursively scan the folder for LIT tests
+ this.scanDirectory(this.testItemRoot, absoluteTestFolderPath);
+ }
+
+ // Function to scan a directory for LIT tests
+ private scanDirectory(parent: vscode.TestItem, directory: string) {
+ const items = fs.readdirSync(directory, { withFileTypes: true });
+
+ for (const item of items) {
+ const itemPath = path.join(directory, item.name);
+
+ if (item.isDirectory()) {
+ // Create a new TestItem for the directory
+ const dirTestItem = this.controller.createTestItem(itemPath, item.name);
+ parent.children.add(dirTestItem);
+
+ // Recursively scan this subdirectory
+ this.scanDirectory(dirTestItem, itemPath);
+ } else if (item.isFile() && this.isLitTestFile(item.name)) {
+ // It's a file and we assume it's a LIT test file
+ const testItem = this.controller.createTestItem(itemPath, item.name, vscode.Uri.file(itemPath));
+ parent.children.add(testItem);
+ }
+ }
+ }
+
+ // A simple helper function to check if a file is a LIT test (now checks for .mlir files)
+ private isLitTestFile(filename: string): boolean {
+ return filename.endsWith('.mlir'); // Now only checks for .mlir files
+ }
+
+ // Function to run a LIT test
+ private async runTest(run: vscode.TestRun, test: vscode.TestItem, token: vscode.CancellationToken) {
+ run.started(test);
+
+ const config = vscode.workspace.getConfiguration('lit');
+ const litToolPath = config.get<string>('lit_path') || 'lit'; // Default to 'lit'
+
+ try {
+ const result = await this.runLitTest(litToolPath, test.uri!.fsPath);
+ if (result.passed) {
+ run.passed(test);
+ } else {
+ run.failed(test, new vscode.TestMessage(result.errorMessage));
+ }
+ } catch (error) {
+ run.errored(test, new vscode.TestMessage(error.message));
+ }
+
+ run.end();
+ }
+
+ // Function to execute the LIT test using the lit tool
+ private async runLitTest(litToolPath: string, testPath: string): Promise<{ passed: boolean, errorMessage?: string }> {
+ const { exec } = require('child_process');
+
+ return new Promise((resolve, reject) => {
+ exec(`${litToolPath} -v ${testPath}`, (error: any, stdout: string, stderr: string) => {
+ if (error) {
+ resolve({ passed: false, errorMessage: `stdout: ${stdout}\stderr: ${stderr}` });
+ } else {
+ resolve({ passed: true });
+ }
+ });
+ });
+ }
+
+ // Helper function to prompt the user for a path
+ private async promptForPath(promptMessage: string): Promise<string | undefined> {
+ return vscode.window.showInputBox({
+ prompt: promptMessage,
+ placeHolder: 'Enter a valid path'
+ });
+ }
+
+ // Implementing the dispose method to clean up resources
+ public dispose() {
+ this.controller.dispose(); // Dispose of the TestController
+ }
+}
diff --git a/mlir/utils/vscode/src/extension.ts b/mlir/utils/vscode/src/extension.ts
index 133fb8f6cf6ae0..e3a480fd87fbb9 100644
--- a/mlir/utils/vscode/src/extension.ts
+++ b/mlir/utils/vscode/src/extension.ts
@@ -3,7 +3,7 @@ import * as vscode from 'vscode';
import {registerMLIRExtensions} from './MLIR/mlir';
import {MLIRContext} from './mlirContext';
import {registerPDLLExtensions} from './PDLL/pdll';
-
+import { LitTestProvider } from './LIT/lit';
/**
* This method is called when the extension is activated. The extension is
* activated the very first time a command is executed.
@@ -15,6 +15,9 @@ export function activate(context: vscode.ExtensionContext) {
const mlirContext = new MLIRContext();
context.subscriptions.push(mlirContext);
+ const litTests = new LitTestProvider(context); // Instantiate the LitTestProvider
+ context.subscriptions.push(litTests); // Push it to context.subscriptions to handle cleanup
+
// Initialize the commands of the extension.
context.subscriptions.push(
vscode.commands.registerCommand('mlir.restart', async () => {
@@ -22,6 +25,9 @@ export function activate(context: vscode.ExtensionContext) {
mlirContext.dispose();
await mlirContext.activate(outputChannel);
}));
+ context.subscriptions.push(vscode.commands.registerCommand('lit.reconfigure', () => {
+ litTests.reconfigureLitSettings();
+ }));
registerMLIRExtensions(context, mlirContext);
registerPDLLExtensions(context, mlirContext);
More information about the Mlir-commits
mailing list