Skip to content
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
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

### Added

* Add support for Ceedling 1.0+ `report_tests_log_factory` plugin with CppUnit format.
* Add `ceedlingExplorer.debugTestExecutablePath` command that returns the full path to test executable.
* Add `isCeedling1Plus()` helper method for reliable Ceedling 1.0+ detection using plugin configuration.
* Add configuration documentation for both Ceedling versions in README.

### Fixed

* Fix compatibility with Ceedling 1.0+ (addresses issues #137 and #145).
* Fix debug executable path detection for Ceedling 1.0+ by checking for `report_tests_log_factory` plugin instead of relying on version string parsing.
* Ensure test executables in subdirectories (e.g., `test_name/test_name.out`) are correctly located for debugging.

## [1.12.0] - 2024-01-02

### Added
Expand Down
39 changes: 38 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ Run your [Ceedling](https://github.com/ThrowTheSwitch/Ceedling) tests using the
* Open the workspace or folder containing your Ceedling project
* Configure your `project.yml` path in the VS Code's settings if required [see below](#options)
* Configure the shell path where Ceedling is installed in the VS Code's settings if required (It might be required on Windows) [see below](#options)
* Enable the `xml_tests_report` Ceedling plugin in your `project.yml` [see the Ceedling doc](https://github.com/ThrowTheSwitch/Ceedling/blob/master/docs/CeedlingPacket.md#tool-element-runtime-substitution-notational-substitution)
* Enable the appropriate Ceedling XML report plugin in your `project.yml`:
* **Ceedling 0.31.x**: Enable the `xml_tests_report` plugin [see the Ceedling doc](https://github.com/ThrowTheSwitch/Ceedling/blob/master/docs/CeedlingPacket.md#tool-element-runtime-substitution-notational-substitution)
* **Ceedling 1.0+**: Enable the `report_tests_log_factory` plugin with `cppunit` report format (see [Configuration Examples](#ceedling-plugin-configuration))
* Open the Test view
* Run your tests using the ![Run](img/run.png) icons in the Test Explorer or the CodeLenses in your test file

Expand Down Expand Up @@ -92,6 +94,41 @@ Example pattern object (GCC compiler warnings):
}
```

### Ceedling Plugin Configuration

This extension supports both Ceedling 0.31.x and Ceedling 1.0+ by accepting either the legacy `xml_tests_report` plugin or the new `report_tests_log_factory` plugin with CppUnit format.

#### Ceedling 0.31.x Configuration

```yaml
:plugins:
:enabled:
- xml_tests_report

# Optional: customize report filename
:xml_tests_report:
:artifact_filename: report.xml # default
```

#### Ceedling 1.0+ Configuration

```yaml
:plugins:
:enabled:
- report_tests_log_factory

:report_tests_log_factory:
:reports:
- cppunit # Required for Test Explorer

# Optional: customize report filename
:report_tests_log_factory:
:cppunit:
:filename: cppunit_tests_report.xml # default
```

**Note**: The extension requires the **CppUnit XML format** specifically. While `report_tests_log_factory` can generate multiple report formats (JUnit, JSON, HTML), only the CppUnit format is compatible with this extension.

## Commands

The following commands are available in VS Code's command palette, use the ID to add them to your keyboard shortcuts:
Expand Down
83 changes: 70 additions & 13 deletions src/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export class CeedlingAdapter implements TestAdapter {
private fileLabelRegex: RegExp | undefined;
private testLabelRegex: RegExp | undefined;
private debugTestExecutable: string = '';
private debugTestExecutablePath: string = '';
private buildDirectory: string = '';
private reportFilename: string = '';
private watchedFileForAutorunList: string[] = [];
Expand Down Expand Up @@ -182,11 +183,12 @@ export class CeedlingAdapter implements TestAdapter {
const testFileName = `${/([^/]*).c$/.exec(testToExec)![1]}`;

// Set current test executable
const ceedlingVersion = await this.getCeedlingVersion();
if (this.detectTestSpecificDefines(ymlProjectData, testFileName) || semver.satisfies(ceedlingVersion, ">0.31.1"))
this.setDebugTestExecutable(`${testFileName}/${testFileName}${ext}`);
else
this.setDebugTestExecutable(`${testFileName}${ext}`);
// Use subdirectory format for Ceedling 1.0+ or tests with specific defines
const useSubdirectory = this.isCeedling1Plus(ymlProjectData) || this.detectTestSpecificDefines(ymlProjectData, testFileName);
const executableRelPath = useSubdirectory ? `${testFileName}/${testFileName}${ext}` : `${testFileName}${ext}`;

this.setDebugTestExecutable(executableRelPath);
this.setDebugTestExecutablePath(`${this.buildDirectory}/test/out/${executableRelPath}`);

// Launch debugger
if (!await vscode.debug.startDebugging(this.workspaceFolder, debugConfiguration))
Expand All @@ -195,6 +197,7 @@ export class CeedlingAdapter implements TestAdapter {
finally {
// Reset current test executable
this.setDebugTestExecutable("");
this.setDebugTestExecutablePath("");
}
}

Expand All @@ -207,6 +210,15 @@ export class CeedlingAdapter implements TestAdapter {
this.logger.info(`Set the debugTestExecutable to ${this.debugTestExecutable}`);
}

getDebugTestExecutablePath(): string {
return this.debugTestExecutablePath;
}

setDebugTestExecutablePath(path: string) {
this.debugTestExecutablePath = path;
this.logger.info(`Set the debugTestExecutablePath to ${this.debugTestExecutablePath}`);
}

async clean(): Promise<void> {
this.logger.trace(`clean()`);
const result = await vscode.window.withProgress(
Expand Down Expand Up @@ -284,14 +296,30 @@ export class CeedlingAdapter implements TestAdapter {
`Please check the ceedlingExplorer.projectPath option.`;
}
try {
if (!ymlProjectData[':plugins'][':enabled'].includes('xml_tests_report')) {
const hasXmlTestsReport = ymlProjectData[':plugins'][':enabled'].includes('xml_tests_report');
const hasReportFactory = ymlProjectData[':plugins'][':enabled'].includes('report_tests_log_factory');

if (!hasXmlTestsReport && !hasReportFactory) {
throw 'Xml report plugin not enabled';
}

// For report_tests_log_factory, verify cppunit format is enabled
if (hasReportFactory && !hasXmlTestsReport) {
try {
const reports = ymlProjectData[':report_tests_log_factory'][':reports'];
if (!reports || !reports.includes('cppunit')) {
throw 'CppUnit report not enabled in report_tests_log_factory';
}
} catch (e) {
return `The 'report_tests_log_factory' plugin is enabled but 'cppunit' report format is not configured. ` +
`Add 'cppunit' to :report_tests_log_factory::reports list in your project.yml file.`;
}
}
} catch (e) {
return `The required Ceedling plugin 'xml_tests_report' is not enabled. ` +
`You have to edit your 'project.xml' file to enable the plugin.\n` +
`see https://github.com/ThrowTheSwitch/Ceedling/blob/master/docs/CeedlingPacket.md` +
`#tool-element-runtime-substitution-notational-substitution`;
return `A required Ceedling XML report plugin is not enabled. ` +
`For Ceedling 0.31.x, enable 'xml_tests_report' plugin. ` +
`For Ceedling 1.0+, enable 'report_tests_log_factory' plugin with 'cppunit' report format.\n` +
`see https://github.com/ThrowTheSwitch/Ceedling/blob/master/docs/CeedlingPacket.md`;
}
}

Expand Down Expand Up @@ -331,7 +359,11 @@ export class CeedlingAdapter implements TestAdapter {
return value.startsWith(" - ");
}).map((value: string) => {
return value.substr(3).trim();
})
}).filter((filePath: string) => {
// Exclude files from the build output directory (preprocessed files, runners, etc.)
// These would create duplicate entries in the test explorer
return !filePath.includes(this.buildDirectory);
});
}
} finally {
release();
Expand Down Expand Up @@ -386,6 +418,15 @@ export class CeedlingAdapter implements TestAdapter {
return false;
}

private isCeedling1Plus(ymlProjectData: any = undefined): boolean {
if (ymlProjectData) {
try {
return ymlProjectData[':plugins'][':enabled'].includes('report_tests_log_factory');
} catch (e) { }
}
return false;
}

private async getCeedlingVersion(): Promise<string> {
const result = await this.execCeedling(['version']);
const regex = new RegExp('^\\s*Ceedling::\\s*(.*)$', 'gm');
Expand Down Expand Up @@ -481,12 +522,28 @@ export class CeedlingAdapter implements TestAdapter {
private setXmlReportPath(ymlProjectData: any = undefined) {
let reportFilename = 'report.xml';
if (ymlProjectData) {
// Try new Ceedling 1.0+ report_tests_log_factory plugin first
try {
const ymlProjectReportFilename = ymlProjectData[':xml_tests_report'][':artifact_filename'];
const ymlProjectReportFilename = ymlProjectData[':report_tests_log_factory'][':cppunit'][':filename'];
if (ymlProjectReportFilename != undefined) {
reportFilename = ymlProjectReportFilename;
}
} catch (e) { }
} catch (e) {
// Fall back to old xml_tests_report plugin (Ceedling 0.31.x)
try {
const ymlProjectReportFilename = ymlProjectData[':xml_tests_report'][':artifact_filename'];
if (ymlProjectReportFilename != undefined) {
reportFilename = ymlProjectReportFilename;
}
} catch (e) {
// If report_tests_log_factory is enabled but no custom filename, use default
try {
if (ymlProjectData[':plugins'][':enabled'].includes('report_tests_log_factory')) {
reportFilename = 'cppunit_tests_report.xml';
}
} catch (e) { }
}
}
}
this.reportFilename = reportFilename;
}
Expand Down
17 changes: 17 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,22 @@ function debugTestExecutable(): string | null {
return null;
}

function debugTestExecutablePath(): string | null {
if (!adapters) return null;
for (let adapter of adapters) {
let debugTestExecutablePath = adapter.getDebugTestExecutablePath();
if (debugTestExecutablePath) {
return debugTestExecutablePath;
}
}
logger.showError("No debug test executable path found");
logger.showInfo(
"A debug configuration with a path containing `${command:ceedlingExplorer.debugTestExecutablePath}` " +
"cannot be started from F5 or the Run pannel. It should be started from a bug icon in the Test pannel."
);
return null;
}

function ceedlingClean(): void {
if (!adapters) return;
for (let adapter of adapters) {
Expand All @@ -41,6 +57,7 @@ export async function activate(context: vscode.ExtensionContext) {
const testExplorerExtension = vscode.extensions.getExtension<TestHub>(testExplorerExtensionId);
if (testExplorerExtension) {
context.subscriptions.push(vscode.commands.registerCommand("ceedlingExplorer.debugTestExecutable", debugTestExecutable));
context.subscriptions.push(vscode.commands.registerCommand("ceedlingExplorer.debugTestExecutablePath", debugTestExecutablePath));
context.subscriptions.push(vscode.commands.registerCommand("ceedlingExplorer.clean", ceedlingClean));
context.subscriptions.push(vscode.commands.registerCommand("ceedlingExplorer.clobber", ceedlingClobber));
context.subscriptions.push(logger);
Expand Down
Loading