Skip to content

Commit

Permalink
Merge pull request #593 from camunda-community-hub/bug/#586_target_fo…
Browse files Browse the repository at this point in the history
…lder_not_used_with_gradle

bug: #586 new configuration options for reporting directory
  • Loading branch information
rohwerj authored Aug 23, 2024
2 parents efbb29b + dd61e41 commit f652d24
Show file tree
Hide file tree
Showing 34 changed files with 440 additions and 64 deletions.
82 changes: 79 additions & 3 deletions docs/user-guide/configuration.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,84 @@
## Setup reporting directory

Optionally, you might configure output path for reports. By default the reports are written to `./target/process-test-coverage/`. To change it you can set the system property `camunda-process-test-coverage.target-dir-root`.
Optionally, you might configure output path for reports. By default, the reports are written to `./target/process-test-coverage/`.
Depending on your preference there are multiple ways to change this.

### Maven
### JUnit5

When registering the extension programmatically, there is an option to change the report directory.
See the following example, that uses the directory build which will typically be used by grade builds.

```kotlin
@RegisterExtension
var extension: ProcessEngineCoverageExtension = ProcessEngineCoverageExtension.builder()
.assertClassCoverageAtLeast(1.0)
.reportDirectory("build/process-test-coverage")
.build()
```

### Spring Testing

For the spring test execution listener, the configuration is provided through a configuration in the application context.
If a bean of type ProcessEngineCoverageProperties is present in the application context, it will be used for configuration
and supports changing the report directory.

```java
@Bean
public ProcessEngineCoverageProperties processEngineCoverageProperties() {
return ProcessEngineCoverageProperties.builder()
.assertClassCoverageAtLeast(1.0)
.reportDirectory("build/process-test-coverage")
.build();
}
```

### Maven aggregation plugin

For the maven aggregation plugin the report directory can be changed via the plugin configuration. It's also
possible to configure the output directory for the aggregation report inside the report directory (by default it's "all").

```xml
<plugin>
<groupId>org.camunda.community.process_test_coverage</groupId>
<artifactId>camunda-process-test-coverage-report-aggregator-maven-plugin</artifactId>
<version>@project.version@</version>
<executions>
<execution>
<id>aggregate-reports</id>
<goals>
<goal>aggregate</goal>
</goals>
<configuration>
<reportDirectory>build/camunda-tests</reportDirectory>
<outputDirectory>aggregation</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
```

### Gradle aggregation plugin

For the gradle plugin it's also possible to change the report directory as well as the output directory for the
aggregation report.
This can be done via an extension setting in the gradle build file.

```groovy
plugins {
id 'org.camunda.community.process_test_coverage.report-aggregator'
}
aggregateProcessTestCoverage {
reportDirectory = 'build/camunda-tests'
outputDirectory = 'aggregation'
}
```

### Legacy way via system property

We still support the old way to configure the reporting directory via system property `camunda-process-test-coverage.target-dir-root`.

#### Maven
```xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand All @@ -15,7 +91,7 @@ Optionally, you might configure output path for reports. By default the reports
</plugin>
```

### Gradle (KTS)
#### Gradle (KTS)
```kotlin
tasks {
withType<Test> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,8 @@ private void handleClassCoverage() {
this.logCoverageDetail(suite);

// Create graphical report
CoverageReportUtil.createReport(this.coverageCollector);
CoverageReportUtil.createJsonReport(this.coverageCollector);
CoverageReportUtil.createReport(this.coverageCollector, null);
CoverageReportUtil.createJsonReport(this.coverageCollector, null);

this.assertCoverage(suiteCoveragePercentage, this.classCoverageAssertionMatchers);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ abstract class ProcessEngineCoverageExtensionBuilder<T>(
var handleTestMethodCoverage: Boolean = true,
var coverageAtLeast: Double? = null,
var excludedProcessDefinitionKeys: List<String> = listOf(),
var optionalAssertCoverageAtLeastProperty: String = DEFAULT_ASSERT_AT_LEAST_PROPERTY
var optionalAssertCoverageAtLeastProperty: String = DEFAULT_ASSERT_AT_LEAST_PROPERTY,
var reportDirectory: String? = null
) {

companion object {
Expand Down Expand Up @@ -70,6 +71,12 @@ abstract class ProcessEngineCoverageExtensionBuilder<T>(
fun optionalAssertCoverageAtLeastProperty(property: String) =
this.apply { optionalAssertCoverageAtLeastProperty = property }

/**
* Specifies the output directory for the reports.
*/
fun reportDirectory(reportDirectory: String) =
this.apply { this.reportDirectory = reportDirectory }

abstract fun build() : T

protected fun coverageFromSystemProperty(key: String): Double? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ class ProcessEngineCoverageExtensionHelper(
/**
* Conditions to be asserted on the individual test method coverages.
*/
private val testMethodNameToCoverageConditions: MutableMap<String, MutableList<Condition<Double>>> = mutableMapOf()
private val testMethodNameToCoverageConditions: MutableMap<String, MutableList<Condition<Double>>> = mutableMapOf(),

/**
* Output directory for the reports.
*/
private val reportDirectory: String? = null

) {

Expand Down Expand Up @@ -84,8 +89,8 @@ class ProcessEngineCoverageExtensionHelper(
logCoverageDetail(suite)

// Create graphical report
CoverageReportUtil.createReport(coverageCollector)
CoverageReportUtil.createJsonReport(coverageCollector)
CoverageReportUtil.createReport(coverageCollector, reportDirectory)
CoverageReportUtil.createJsonReport(coverageCollector, reportDirectory)

assertCoverage(suiteCoveragePercentage, classCoverageAssertionConditions)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ class ProcessEngineCoverageExtension(
*/
private val excludedProcessDefinitionKeys: List<String> = listOf(),

/**
* Output directory for the reports.
*/
private val reportDirectory: String? = null

) : ProcessEngineExtension(), BeforeAllCallback, AfterAllCallback {

companion object : KLogging() {
Expand Down Expand Up @@ -82,7 +87,7 @@ class ProcessEngineCoverageExtension(

private val processEngineCoverageExtensionHelper = ProcessEngineCoverageExtensionHelper(coverageCollector,
detailedCoverageLogging, handleTestMethodCoverage, excludedProcessDefinitionKeys,
classCoverageAssertionConditions, testMethodNameToCoverageConditions)
classCoverageAssertionConditions, testMethodNameToCoverageConditions, reportDirectory)

override fun postProcessTestInstance(testInstance: Any?, context: ExtensionContext) {
super.postProcessTestInstance(testInstance, context)
Expand Down Expand Up @@ -160,7 +165,8 @@ class ProcessEngineCoverageExtension(
return ProcessEngineCoverageExtension(
detailedCoverageLogging = detailedCoverageLogging,
handleTestMethodCoverage = handleTestMethodCoverage,
excludedProcessDefinitionKeys = excludedProcessDefinitionKeys
excludedProcessDefinitionKeys = excludedProcessDefinitionKeys,
reportDirectory = reportDirectory
).apply {
coverageFromSystemProperty(this@Builder.optionalAssertCoverageAtLeastProperty)?.let {
addClassCoverageAtLeast(it)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,12 @@ class ProcessEngineCoverageExtension(
*/
private val excludedProcessDefinitionKeys: List<String> = listOf(),

) : BeforeAllCallback, AfterAllCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback {
/**
* Output directory for the reports.
*/
private val reportDirectory: String? = null

) : BeforeAllCallback, AfterAllCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback {

companion object : KLogging() {
@JvmStatic
Expand Down Expand Up @@ -82,7 +87,7 @@ class ProcessEngineCoverageExtension(

private val processEngineCoverageExtensionHelper = ProcessEngineCoverageExtensionHelper(coverageCollector,
detailedCoverageLogging, handleTestMethodCoverage, excludedProcessDefinitionKeys,
classCoverageAssertionConditions, testMethodNameToCoverageConditions)
classCoverageAssertionConditions, testMethodNameToCoverageConditions, reportDirectory)

/**
* Handles creating the run if a relevant test method is called.
Expand Down Expand Up @@ -134,7 +139,8 @@ class ProcessEngineCoverageExtension(
return ProcessEngineCoverageExtension(
detailedCoverageLogging = detailedCoverageLogging,
handleTestMethodCoverage = handleTestMethodCoverage,
excludedProcessDefinitionKeys = excludedProcessDefinitionKeys
excludedProcessDefinitionKeys = excludedProcessDefinitionKeys,
reportDirectory = reportDirectory
).apply {
coverageFromSystemProperty(this@Builder.optionalAssertCoverageAtLeastProperty)?.let {
addClassCoverageAtLeast(it)
Expand Down
3 changes: 2 additions & 1 deletion extension/report-aggregator-gradle-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ dependencies {
implementation(group = "org.camunda.community.process_test_coverage", name = "camunda-process-test-coverage-report-generator", version = "$version")
testImplementation(gradleTestKit())
testImplementation(group = "org.junit.jupiter", name = "junit-jupiter", version = "5.10.3")
testRuntimeOnly(group = "org.junit.platform", name = "junit-platform-launcher")
testImplementation(group = "org.assertj", name = "assertj-core", version = "3.26.3")
}

tasks.withType<Test>().configureEach {
tasks.test {
useJUnitPlatform()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,31 @@ package org.camunda.community.process_test_coverage.report.aggregator
import org.camunda.community.process_test_coverage.core.export.CoverageStateJsonExporter.combineCoverageStateResults
import org.camunda.community.process_test_coverage.core.export.CoverageStateJsonExporter.createCoverageStateResult
import org.camunda.community.process_test_coverage.core.export.CoverageStateJsonExporter.readCoverageStateResult
import org.camunda.community.process_test_coverage.core.export.CoverageStateResult
import org.camunda.community.process_test_coverage.report.CoverageReportUtil
import org.gradle.api.Plugin
import org.gradle.api.Project
import java.io.File

abstract class ReportAggregatorPluginExtension {
var outputDirectory: String? = null
var reportDirectory: String = "build/process-test-coverage"
var outputDirectory: String = "all"
}

class ReportAggregatorPlugin : Plugin<Project> {

companion object {
var TARGET_DIR_ROOT: String = System.getProperty("camunda-process-test-coverage.target-dir-root", "target/process-test-coverage/")
}

override fun apply(project: Project) {
val outputDirectory = calcOutputDirectory(project)
val extension = project.extensions.create("aggregateProcessTestCoverage", ReportAggregatorPluginExtension::class.java)
project.task("aggregateProcessTestCoverage")
.doLast {
val outputDirectory = File(File(project.projectDir, extension.reportDirectory), extension.outputDirectory)
project.allprojects
.asSequence()
.map {
project.logger.debug("Processing module ${it.name}")
it.fileTree(".").apply { this.include("$TARGET_DIR_ROOT/**/report.json", "**/all/report.json") }.files
it.fileTree(it.projectDir).apply {
this.include("${extension.reportDirectory}/**/report.json")
this.exclude("**/${extension.outputDirectory}/report.json")
}.files
}
.flatten()
.map {
Expand All @@ -67,9 +67,4 @@ class ReportAggregatorPlugin : Plugin<Project> {
}
}

private fun calcOutputDirectory(project: Project): String {
val extension = project.extensions.create("aggregateProcessTestCoverage", ReportAggregatorPluginExtension::class.java)
return extension.outputDirectory ?: File(project.projectDir, TARGET_DIR_ROOT + "all").absolutePath
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,15 @@ class ReportAggregatorPluginTest {
gradleRunner = GradleRunner.create()
.withProjectDir(testProjectDir)
.withPluginClasspath()
.forwardOutput()
.withArguments("aggregateProcessTestCoverage")
}

@Test
fun should_not_create_any_file_when_run_on_empty_dir() {
val result = gradleRunner.build()
assertEquals(TaskOutcome.SUCCESS, result.task(":aggregateProcessTestCoverage")?.outcome)
assertThat(File(testProjectDir, "target/process-test-coverage/all/report.json"))
assertThat(File(testProjectDir, "build/process-test-coverage/all/report.json"))
.doesNotExist()
}

Expand Down Expand Up @@ -114,8 +115,28 @@ class ReportAggregatorPluginTest {
assertResult()
}

private fun assertResult() {
assertThat(File(testProjectDir, "target/process-test-coverage/all/report.json"))
@Test
fun should_generate_expected_result_when_run_with_different_report_directory() {
copyDirectory(Paths.get("src/test/resources/different_report_directory/"), testProjectDir.toPath())
val buildFileContent = """
plugins {
id 'org.camunda.community.process_test_coverage.report-aggregator'
}
aggregateProcessTestCoverage {
reportDirectory = 'build/camunda-tests'
outputDirectory = 'aggregation'
}
"""
Files.write(buildFile.toPath(), buildFileContent.toByteArray())
val result = gradleRunner.build()
assertEquals(TaskOutcome.SUCCESS, result.task(":aggregateProcessTestCoverage")?.outcome)
assertResult("build/camunda-tests/aggregation/report.json")
}

private fun assertResult(expectedFile: String = "build/process-test-coverage/all/report.json") {
assertThat(File(testProjectDir, expectedFile))
.exists().isFile
.content()
.satisfies(Consumer {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"suites":[{"id":"[engine:junit-jupiter]/[class:org.camunda.community.process_test_coverage.report.aggregator.FirstTest]","name":"org.camunda.community.process_test_coverage.report.aggregator.FirstTest","runs":[{"id":"[engine:junit-jupiter]/[class:org.camunda.community.process_test_coverage.report.aggregator.FirstTest]/[method:should_have_100_percent_coverage]","name":"should_have_100_percent_coverage","events":[{"source":"FLOW_NODE","type":"START","definitionKey":"StartEvent_1","elementType":"startEvent","modelKey":"Testprocess","timestamp":1654807399},{"source":"FLOW_NODE","type":"END","definitionKey":"StartEvent_1","elementType":"startEvent","modelKey":"Testprocess","timestamp":1654807399},{"source":"SEQUENCE_FLOW","type":"TAKE","definitionKey":"Flow_0fnqvo6","elementType":"sequenceFlow","modelKey":"Testprocess","timestamp":1654807399},{"source":"FLOW_NODE","type":"START","definitionKey":"Event_0l5zycp","elementType":"endEvent","modelKey":"Testprocess","timestamp":1654807399},{"source":"FLOW_NODE","type":"END","definitionKey":"Event_0l5zycp","elementType":"endEvent","modelKey":"Testprocess","timestamp":1654807399}]}]}],"models":[{"key":"Testprocess","totalElementCount":3,"xml":"\u003c?xmlversion\u003d\"1.0\"encoding\u003d\"UTF-8\"standalone\u003d\"no\"?\u003e\n\u003cbpmn:definitionsxmlns:bpmn\u003d\"http://www.omg.org/spec/BPMN/20100524/MODEL\"xmlns:bpmndi\u003d\"http://www.omg.org/spec/BPMN/20100524/DI\"xmlns:dc\u003d\"http://www.omg.org/spec/DD/20100524/DC\"xmlns:di\u003d\"http://www.omg.org/spec/DD/20100524/DI\"xmlns:modeler\u003d\"http://camunda.org/schema/modeler/1.0\"exporter\u003d\"CamundaModeler\"exporterVersion\u003d\"5.0.0\"expressionLanguage\u003d\"http://www.w3.org/1999/XPath\"id\u003d\"Definitions_1r3mlfs\"modeler:executionPlatform\u003d\"CamundaPlatform\"modeler:executionPlatformVersion\u003d\"7.17.0\"targetNamespace\u003d\"http://bpmn.io/schema/bpmn\"typeLanguage\u003d\"http://www.w3.org/2001/XMLSchema\"\u003e\n\n\u003cbpmn:processid\u003d\"Testprocess\"isClosed\u003d\"false\"isExecutable\u003d\"true\"processType\u003d\"None\"\u003e\n\n\u003cbpmn:startEventid\u003d\"StartEvent_1\"isInterrupting\u003d\"true\"name\u003d\"Start\"parallelMultiple\u003d\"false\"\u003e\n\n\u003cbpmn:outgoing\u003eFlow_0fnqvo6\u003c/bpmn:outgoing\u003e\n\n\u003c/bpmn:startEvent\u003e\n\n\u003cbpmn:endEventid\u003d\"Event_0l5zycp\"name\u003d\"End\"\u003e\n\n\u003cbpmn:incoming\u003eFlow_0fnqvo6\u003c/bpmn:incoming\u003e\n\n\u003c/bpmn:endEvent\u003e\n\n\u003cbpmn:sequenceFlowid\u003d\"Flow_0fnqvo6\"sourceRef\u003d\"StartEvent_1\"targetRef\u003d\"Event_0l5zycp\"/\u003e\n\n\u003c/bpmn:process\u003e\n\n\u003cbpmndi:BPMNDiagramid\u003d\"BPMNDiagram_1\"\u003e\n\n\u003cbpmndi:BPMNPlanebpmnElement\u003d\"Testprocess\"id\u003d\"BPMNPlane_1\"\u003e\n\n\u003cbpmndi:BPMNEdgebpmnElement\u003d\"Flow_0fnqvo6\"id\u003d\"Flow_0fnqvo6_di\"\u003e\n\n\u003cdi:waypointx\u003d\"215\"y\u003d\"97\"/\u003e\n\n\u003cdi:waypointx\u003d\"272\"y\u003d\"97\"/\u003e\n\n\u003c/bpmndi:BPMNEdge\u003e\n\n\u003cbpmndi:BPMNShapebpmnElement\u003d\"StartEvent_1\"id\u003d\"_BPMNShape_StartEvent_2\"\u003e\n\n\u003cdc:Boundsheight\u003d\"36\"width\u003d\"36\"x\u003d\"179\"y\u003d\"79\"/\u003e\n\n\u003cbpmndi:BPMNLabel\u003e\n\n\u003cdc:Boundsheight\u003d\"14\"width\u003d\"28\"x\u003d\"183\"y\u003d\"122\"/\u003e\n\n\u003c/bpmndi:BPMNLabel\u003e\n\n\u003c/bpmndi:BPMNShape\u003e\n\n\u003cbpmndi:BPMNShapebpmnElement\u003d\"Event_0l5zycp\"id\u003d\"Event_0l5zycp_di\"\u003e\n\n\u003cdc:Boundsheight\u003d\"36\"width\u003d\"36\"x\u003d\"272\"y\u003d\"79\"/\u003e\n\n\u003cbpmndi:BPMNLabel\u003e\n\n\u003cdc:Boundsheight\u003d\"14\"width\u003d\"21\"x\u003d\"280\"y\u003d\"122\"/\u003e\n\n\u003c/bpmndi:BPMNLabel\u003e\n\n\u003c/bpmndi:BPMNShape\u003e\n\n\u003c/bpmndi:BPMNPlane\u003e\n\n\u003c/bpmndi:BPMNDiagram\u003e\n\n\u003c/bpmn:definitions\u003e\n"}]}
Loading

0 comments on commit f652d24

Please sign in to comment.