diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f6f0063..4f6f6112 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +## [4.8.3] + +### Added + +- `testMate.test.advancedExecutables` - `executableSuffixToInclude` for [custom filtering](https://github.com/matepek/vscode-catch2-test-adapter/issues/415). + ## [4.8.2] - 2024-02-07 ### Fixed diff --git a/documents/configuration/test.advancedExecutables.md b/documents/configuration/test.advancedExecutables.md index 44f2d6bf..3c08bf5d 100644 --- a/documents/configuration/test.advancedExecutables.md +++ b/documents/configuration/test.advancedExecutables.md @@ -44,27 +44,28 @@ This variable can be If it is an object it can contains the following properties: -| Property | Description | -| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `name` | The name of the test suite (file). Can contains variables related to `pattern`. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | -| `pattern` | A relative (to workspace directory) or an absolute path or [_glob pattern_](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options). ⚠️**Avoid backslash!**: NO `\\`; (required) [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | -| `description` | A less prominent text after the `name`. Can contains variables related to `pattern`. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | -| `cwd` | The current working directory for the test executable. If it isn't provided and `test.workingDirectory` does then that will be used. Can contains variables related to `pattern`. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | -| `env` | Environment variables for the test executable. Can contains variables related to `pattern` and variables related to the process's environment variables (Ex.: `${os_env:PATH}`). [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | -| `envFile` | File containing environment variables for the test executable. (JSON object or .env file) [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | -| `executionWrapper` | Specifies an executor which wraps the executions. Useful for emulators. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | -| `sourceFileMap` | Replaces the key with the value in the souce file path. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | -| `dependsOn` | Array of (relative / absolute) _paths_ / [_glob pattern_](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options) (string[]). If a related file is _changed/created/deleted_ and autorun is enabled in "..." menu it will run the related executables. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | -| `runTask` | Tasks to run before running/debugging tests. The task should be defined like any other task in vscode (e.g. in tasks.json). If the task exits with a non-zero code, execution of tests will be halted. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | -| `parallelizationLimit` | The variable maximize the number of the parallel execution of one executable instance. Note: `testMate.cpp.test.parallelExecutionLimit` is a global limit. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | -| `strictPattern` | Test loading fails if one of the files matched by `pattern` is not a test executable. (Helps noticing unexpected crashes/problems under test loading.) | -| `markAsSkipped` | If true then all the tests related to the pattern are skipped. They can be run manually though. | -| `executableCloning` | (experimental) If enabled it creates a copy of the test executable before listing or running the tests. NOTE: discovery (`--help`) still uses the original file. | -| `waitForBuildProcess` | Prevents the extension of auto-reloading. With this linking failure might can be avoided. Can be true to use a default pattern that works for most cases, or a string to pass your own search pattern (regex) for processes. | -| `catch2` | Object with framework specific settings. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md#framework-specific-settings) | -| `gtest` | Object with framework specific settings. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md#framework-specific-settings) | -| `doctest` | Object with framework specific settings. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md#framework-specific-settings) | -| `gbenchmark` | Object with framework specific settings. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md#framework-specific-settings) | +| Property | Description | +| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | The name of the test suite (file). Can contains variables related to `pattern`. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | +| `pattern` | A relative (to workspace directory) or an absolute path or [_glob pattern_](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options). ⚠️**Avoid backslash!**: NO `\\`; (required) [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | +| `description` | A less prominent text after the `name`. Can contains variables related to `pattern`. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | +| `cwd` | The current working directory for the test executable. If it isn't provided and `test.workingDirectory` does then that will be used. Can contains variables related to `pattern`. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | +| `env` | Environment variables for the test executable. Can contains variables related to `pattern` and variables related to the process's environment variables (Ex.: `${os_env:PATH}`). [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | +| `envFile` | File containing environment variables for the test executable. (JSON object or .env file) [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | +| `executionWrapper` | Specifies an executor which wraps the executions. Useful for emulators. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | +| `sourceFileMap` | Replaces the key with the value in the souce file path. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | +| `dependsOn` | Array of (relative / absolute) _paths_ / [_glob pattern_](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options) (string[]). If a related file is _changed/created/deleted_ and autorun is enabled in "..." menu it will run the related executables. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | +| `runTask` | Tasks to run before running/debugging tests. The task should be defined like any other task in vscode (e.g. in tasks.json). If the task exits with a non-zero code, execution of tests will be halted. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | +| `parallelizationLimit` | The variable maximize the number of the parallel execution of one executable instance. Note: `testMate.cpp.test.parallelExecutionLimit` is a global limit. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md) | +| `strictPattern` | Test loading fails if one of the files matched by `pattern` is not a test executable. (Helps noticing unexpected crashes/problems under test loading.) | +| `markAsSkipped` | If true then all the tests related to the pattern are skipped. They can be run manually though. | +| `executableCloning` | If enabled it creates a copy of the test executable before listing or running the tests. NOTE: discovery (`--help`) still uses the original file. | +| `executableSuffixToInclude` | Filter files based on suffix for faster discovery. | +| `waitForBuildProcess` | Prevents the extension of auto-reloading. With this linking failure might can be avoided. Can be true to use a default pattern that works for most cases, or a string to pass your own search pattern (regex) for processes. | +| `catch2` | Object with framework specific settings. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md#framework-specific-settings) | +| `gtest` | Object with framework specific settings. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md#framework-specific-settings) | +| `doctest` | Object with framework specific settings. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md#framework-specific-settings) | +| `gbenchmark` | Object with framework specific settings. [Detail](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/documents/configuration/test.advancedExecutables.md#framework-specific-settings) | The `pattern` (or the `executables` used as string or an array of strings) can contain [_search-pattern_](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options). diff --git a/package.json b/package.json index 9b23a53c..943ad55c 100644 --- a/package.json +++ b/package.json @@ -345,10 +345,17 @@ "default": false }, "executableCloning": { - "markdownDescription": "(experimental) If enabled it creates a copy of the test executable before listing or running the tests. NOTE: discovery (`--help`) still uses the original file.", + "markdownDescription": "If enabled it creates a copy of the test executable before listing or running the tests. NOTE: discovery (`--help`) still uses the original file.", "type": "boolean", "default": false }, + "executableSuffixToInclude": { + "markdownDescription": "Filter files based on suffix for faster discovery.", + "type": "array", + "items": { + "type": "string" + } + }, "waitForBuildProcess": { "markdownDescription": "Prevents the extension of auto-reloading. With this linking failure might can be avoided. Can be true to use a default pattern that works for most cases, or a string to pass your own search pattern (regex) for processes.", "type": [ diff --git a/src/AdvancedExecutableInterface.ts b/src/AdvancedExecutableInterface.ts index 87625cca..778d82d5 100644 --- a/src/AdvancedExecutableInterface.ts +++ b/src/AdvancedExecutableInterface.ts @@ -18,6 +18,7 @@ export type AdvancedExecutableConfig = { strictPattern?: boolean; markAsSkipped?: boolean; executableCloning?: boolean; + executableSuffixToInclude?: string[]; waitForBuildProcess?: boolean | string; catch2?: FrameworkSpecificConfig; gtest?: FrameworkSpecificConfig; diff --git a/src/ConfigOfExecGroup.ts b/src/ConfigOfExecGroup.ts index f5a81a5f..327d66c4 100644 --- a/src/ConfigOfExecGroup.ts +++ b/src/ConfigOfExecGroup.ts @@ -41,12 +41,56 @@ export class ConfigOfExecGroup implements vscode.Disposable { private readonly _strictPattern: boolean | undefined, private readonly _markAsSkipped: boolean | undefined, private readonly _executableCloning: boolean | undefined, + executableSuffixToInclude: string[] | undefined, private readonly _waitForBuildProcess: boolean | string | undefined, private readonly _executionWrapper: ExecutionWrapperConfig | undefined, private readonly _sourceFileMap: Record, private readonly _frameworkSpecific: Record, - ) {} + ) { + this._executableSuffixToInclude = + process.platform === 'win32' ? new Set(executableSuffixToInclude ?? ['.exe', '.cmd', '.bat']) : undefined; + // https://askubuntu.com/questions/156392/what-is-the-equivalent-of-an-exe-file + this._executableSuffixToExclude = + process.platform !== 'win32' + ? new Set([ + '.a', + '.bat', + '.c', + '.cc', + '.cmake', + '.cpp', + '.cxx', + '.deb', + '.dir', + '.gz', + '.h', + '.hpp', + '.hxx', + '.in', + '.input', + '.ko', + '.log', + '.md', + '.mm', + '.ninja', + '.o', + '.obj', + '.pc', + '.php', + '.pyc', + '.rpm', + '.so', + '.stamp', + '.tar', + '.txt', + '.vcxproj.user', + '.xml', + ]) + : undefined; + } + private readonly _executableSuffixToInclude: Set | undefined; + private readonly _executableSuffixToExclude: Set | undefined; private _disposables: vscode.Disposable[] = []; dispose(): void { @@ -132,7 +176,7 @@ export class ConfigOfExecGroup implements vscode.Disposable { suiteCreationAndLoadingTasks.push( (async (): Promise => { try { - await c2fs.isNativeExecutableAsync(file); + await c2fs.isNativeExecutableAsync(file, this._executableSuffixToInclude, this._executableSuffixToExclude); try { const factory = await this._createSuiteByUri(file); const suite = await factory.create(false); @@ -373,6 +417,8 @@ export class ConfigOfExecGroup implements vscode.Disposable { this._parallelizationLimit, this._markAsSkipped === true, this._executableCloning === true, + this._executableSuffixToInclude, + this._executableSuffixToExclude, this._runTask, spawner, resolvedSourceFileMap, @@ -434,10 +480,12 @@ export class ConfigOfExecGroup implements vscode.Disposable { return; } - const isExec = await c2fs.isNativeExecutableAsync(filePath).then( - () => true, - () => false, - ); + const isExec = await c2fs + .isNativeExecutableAsync(filePath, this._executableSuffixToInclude, this._executableSuffixToExclude) + .then( + () => true, + () => false, + ); if (isExec) { try { @@ -511,10 +559,12 @@ export class ConfigOfExecGroup implements vscode.Disposable { } else { await promisify(setTimeout)(delay); - const isExec = await c2fs.isNativeExecutableAsync(filePath).then( - () => true, - () => false, - ); + const isExec = await c2fs + .isNativeExecutableAsync(filePath, this._executableSuffixToInclude, this._executableSuffixToExclude) + .then( + () => true, + () => false, + ); return this._recursiveHandleRunnable(executable, isExec, Math.min(delay * 2, 2000)); } diff --git a/src/Configurations.ts b/src/Configurations.ts index 36fb1d08..070b82ef 100644 --- a/src/Configurations.ts +++ b/src/Configurations.ts @@ -464,6 +464,7 @@ export class Configurations { undefined, undefined, undefined, + undefined, {}, { catch2: {}, @@ -569,6 +570,8 @@ export class Configurations { const executableCloning: boolean | undefined = obj.executableCloning; + const executableSuffixToInclude: string[] | undefined = obj.executableSuffixToInclude; + const waitForBuildProcess: boolean | string | undefined = obj.waitForBuildProcess; const defaultTestGrouping = obj.testGrouping; @@ -601,6 +604,7 @@ export class Configurations { strictPattern, markAsSkipped, executableCloning, + executableSuffixToInclude, waitForBuildProcess, spawnerConfig, sourceFileMap, diff --git a/src/framework/ExecutableFactory.ts b/src/framework/ExecutableFactory.ts index 7fed0514..cbe64d47 100644 --- a/src/framework/ExecutableFactory.ts +++ b/src/framework/ExecutableFactory.ts @@ -23,6 +23,8 @@ export class ExecutableFactory { private readonly _parallelizationLimit: number, private readonly _markAsSkipped: boolean, private readonly _executableCloning: boolean, + private readonly _executableSuffixToInclude: Set | undefined, + private readonly _executableSuffixToExclude: Set | undefined, private readonly _runTask: RunTaskConfig, private readonly _spawner: Spawner, private readonly _resolvedSourceFileMap: Record, @@ -31,7 +33,12 @@ export class ExecutableFactory { async create(checkIsNativeExecutable: boolean): Promise { const runWithHelpRes = await this._shared.taskPool.scheduleTask(async () => { - if (checkIsNativeExecutable) await c2fs.isNativeExecutableAsync(this._execPath); + if (checkIsNativeExecutable) + await c2fs.isNativeExecutableAsync( + this._execPath, + this._executableSuffixToInclude, + this._executableSuffixToExclude, + ); return this._spawner.spawnAsync(this._execPath, ['--help'], this._execOptions, this._shared.execParsingTimeout); }); diff --git a/src/util/FSWrapper.ts b/src/util/FSWrapper.ts index 951b51fa..d8087db5 100644 --- a/src/util/FSWrapper.ts +++ b/src/util/FSWrapper.ts @@ -42,60 +42,22 @@ function accessAsync(filePath: string, flag: number): Promise { }); } -// https://askubuntu.com/questions/156392/what-is-the-equivalent-of-an-exe-file -const nativeExecutableExtensionFilter = new Set([ - '.a', - '.bat', - '.c', - '.cc', - '.cmake', - '.cpp', - '.cxx', - '.deb', - '.dir', - '.gz', - '.h', - '.hpp', - '.hxx', - '.in', - '.input', - '.ko', - '.log', - '.md', - '.mm', - '.ninja', - '.o', - '.obj', - '.pc', - '.php', - '.pyc', - '.rpm', - '.so', - '.stamp', - '.tar', - '.txt', - '.vcxproj.user', - '.xml', -]); - -const win32NativeExecutableExtensionFilter = new Set(['.exe', '.cmd', '.bat']); - -export function isNativeExecutableAsync(filePath: string): Promise { +export function isNativeExecutableAsync( + filePath: string, + extensionIncludeFilter: Set | undefined, + extensionExcludeFilter: Set | undefined, +): Promise { const ext = path.extname(filePath); - if (process.platform === 'win32') { - if (win32NativeExecutableExtensionFilter.has(ext)) return accessAsync(filePath, ExecutableFlag); - else return Promise.reject(new Error('Not a native executable extension on win32: ' + filePath)); - } else { - if (filePath.endsWith('/')) { - // noted that we got ".../CMakeFiles/" a lot. I assume the slash means directory. - return Promise.reject(new Error('It is a directory, not a native executable: ' + filePath)); - } - if (nativeExecutableExtensionFilter.has(ext)) { - return Promise.reject(new Error('Not a native executable (filtered because of its extension): ' + filePath)); - } else { - return accessAsync(filePath, ExecutableFlag); - } + if (extensionIncludeFilter) { + if (!extensionIncludeFilter.has(ext)) return Promise.reject(new Error('Not included by filter: ' + filePath)); + } else if (extensionExcludeFilter) { + if (extensionExcludeFilter.has(ext)) return Promise.reject(new Error('Excluded by fitler: ' + filePath)); + } + if (process.platform !== 'win32' && filePath.endsWith('/')) { + // noted that we got ".../CMakeFiles/" a lot. I assume the slash means directory. + return Promise.reject(new Error('It is a directory, not a native executable: ' + filePath)); } + return accessAsync(filePath, ExecutableFlag); } export function existsSync(filePath: string): boolean {