Skip to content

Commit

Permalink
Improved testing of maven plugin (#644)
Browse files Browse the repository at this point in the history
### What's done:
* More unit tests
* Added jacoco agent via .mvn/jvm.config
* Configured jacoco to generate reports for integration tests
* Integration tests on diktat:fix
* Changed !== to !=
* resolveConfig returns non-nullable
  • Loading branch information
petertrr authored Dec 21, 2020
1 parent f6786c2 commit e71d22e
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 35 deletions.
51 changes: 41 additions & 10 deletions diktat-maven-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -161,21 +161,36 @@
</executions>
</plugin>
<plugin>
<!-- override jacoco config to include integration test results -->
<!-- Jacoco configuration for report on integration test, doesn't override configuration for unit tests -->
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<configuration>
<excludes>
<!-- HelpMojo is generated automatically -->
<exclude>**/HelpMojo*</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>report-integration</id>
<goals>
<goal>prepare-agent</goal>
<!-- Merge reports of individual maven invokations and build xml report -->
<goal>merge</goal>
<goal>report-integration</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>post-integration-test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<fileSets>
<fileSet>
<directory>${project.build.directory}</directory>
<includes>
<!-- Every test case runs maven process and therefore generates individual execution report -->
<inlcude>jacoco-it*.exec</inlcude>
</includes>
</fileSet>
</fileSets>
<destFile>${project.build.directory}/jacoco-it.exec</destFile>
</configuration>
</execution>
</executions>
</plugin>
Expand All @@ -185,7 +200,23 @@
<directory>../examples/maven</directory>
<!-- setting filtering to false keeps unresolved properties, which in diktat-examples do not depend on diktat properties -->
<filtering>false</filtering>
<targetPath>${project.build.testOutputDirectory}/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest/maven</targetPath>
<targetPath>${project.build.testOutputDirectory}/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest/diktatCheck</targetPath>
</testResource>
<testResource>
<directory>${project.basedir}/src/test/resources/.mvn</directory>
<!-- Here we need to resolve actual maven properties -->
<filtering>true</filtering>
<targetPath>${project.build.testOutputDirectory}/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest/diktatCheck/.mvn</targetPath>
</testResource>
<testResource>
<directory>../examples/maven</directory>
<filtering>false</filtering>
<targetPath>${project.build.testOutputDirectory}/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest/diktatFix</targetPath>
</testResource>
<testResource>
<directory>${project.basedir}/src/test/resources/.mvn</directory>
<filtering>true</filtering>
<targetPath>${project.build.testOutputDirectory}/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest/diktatFix/.mvn</targetPath>
</testResource>
</testResources>
</build>
Expand Down Expand Up @@ -300,4 +331,4 @@
</build>
</profile>
</profiles>
</project>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ abstract class DiktatBaseMojo : AbstractMojo() {
override fun execute() {
val configFile = resolveConfig()
if (!File(configFile).exists()) {
throw MojoExecutionException("Configuration file $configFile doesn't exist")
throw MojoExecutionException("Configuration file $diktatConfigFile doesn't exist")
}
log.info("Running diKTat plugin with configuration file $configFile and inputs $inputs" +
if (excludes.isNotEmpty()) " and excluding $excludes" else ""
Expand All @@ -90,14 +90,23 @@ abstract class DiktatBaseMojo : AbstractMojo() {
}
}

/**
* Function that searches diktat config file in maven project hierarchy.
* If [diktatConfigFile] is absolute, it's path is used. If [diktatConfigFile] is relative, this method looks for it in all maven parent projects.
* This way config file can be placed in parent module directory and used in all child modules too.
*
* @return path to configuration file as a string. File by this path might not exist.
*/
private fun resolveConfig(): String {
if (File(diktatConfigFile).isAbsolute) {
return diktatConfigFile
}

return generateSequence(mavenProject) { it.parent }
.map { File(it.basedir, diktatConfigFile) }
.first { it.exists() }
.run {
firstOrNull { it.exists() } ?: first()
}
.absolutePath
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import java.io.File
* Main [Mojo] that call [DiktatRuleSetProvider]'s rules on [inputs] files
*/
@Mojo(name = "check")
@Suppress("unused")
class DiktatCheckMojo : DiktatBaseMojo() {
/**
* @param params instance of [KtLint.Params] used in analysis
Expand All @@ -29,6 +30,7 @@ class DiktatCheckMojo : DiktatBaseMojo() {
* and fixes discovered errors
*/
@Mojo(name = "fix")
@Suppress("unused")
class DiktatFixMojo : DiktatBaseMojo() {
/**
* @param params instance of [KtLint.Params] used in analysis
Expand All @@ -38,7 +40,7 @@ class DiktatFixMojo : DiktatBaseMojo() {
val filePath = params.userData["file_path"] ?: error("File path should be provided")
val fileContent = File(filePath).readText(charset("UTF-8"))
val formattedText = KtLint.format(params)
if (fileContent !== formattedText) {
if (fileContent != formattedText) {
log.info("Original and formatted content differ, writing to $fileName...")
File(filePath).writeText(formattedText, charset("UTF-8"))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,45 @@
package org.cqfn.diktat.plugin.maven

import org.apache.maven.execution.DefaultMavenExecutionRequest
import org.apache.maven.plugin.MojoExecutionException
import org.apache.maven.plugin.testing.MojoRule
import org.junit.Assert
import org.apache.maven.project.ProjectBuilder
import org.apache.maven.project.ProjectBuildingRequest
import org.eclipse.aether.DefaultRepositorySystemSession
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.jupiter.api.Assertions
import kotlin.io.path.ExperimentalPathApi
import kotlin.io.path.Path
import kotlin.io.path.createTempFile
import kotlin.io.path.writeText
import kotlin.io.path.div

/**
* Tests for mojo configuration
* FixMe: inject project version from outside
* FixMe: `@Parameter` properties are not initialized with default values
* Tests for mojo configuration. NB: this tests are using Junit4, because maven-plugin-testing-harness doesn't support 5.
*/
@OptIn(ExperimentalPathApi::class)
@Suppress("TOO_LONG_FUNCTION")
@Suppress("LongMethod", "TOO_LONG_FUNCTION")
class DiktatBaseMojoTest {
@get:Rule
val mojoRule = MojoRule()
@get:Rule val mojoRule = MojoRule()
private lateinit var buildingRequest: ProjectBuildingRequest
private lateinit var projectBuilder: ProjectBuilder

/**
* Initialize properties needed to create mavenProject stub able to resolve maven parameters.
*/
@Before
fun setUp() {
val executionRequest = DefaultMavenExecutionRequest()
buildingRequest = executionRequest.projectBuildingRequest.apply {
repositorySession = DefaultRepositorySystemSession()
}
projectBuilder = mojoRule.lookup(ProjectBuilder::class.java)
}

@Test
fun `test plugin configuration`() {
val pom = createTempFile()
fun `test default plugin configuration`() {
val pom = createTempFile().toFile()
pom.writeText(
"""
<project xmlns="http://maven.apache.org/POM/4.0.0"
Expand All @@ -31,15 +49,64 @@ class DiktatBaseMojoTest {
<groupId>org.cqfn.diktat</groupId>
<artifactId>diktat-test</artifactId>
<version>0.1.6-SNAPSHOT</version>
<version>1.0.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.cqfn.diktat</groupId>
<artifactId>diktat-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
""".trimIndent()
)
val mavenProject = projectBuilder.build(pom, buildingRequest).project

val diktatCheckMojo = mojoRule.lookupConfiguredMojo(mavenProject, "check") as DiktatCheckMojo
Assertions.assertFalse(diktatCheckMojo.debug)
Assertions.assertEquals("diktat-analysis.yml", diktatCheckMojo.diktatConfigFile)
Assertions.assertIterableEquals(listOf(pom.parentFile.toPath() / "src"), diktatCheckMojo.inputs.map { Path(it) })
Assertions.assertTrue(diktatCheckMojo.excludes.isEmpty())
}

@Test
fun `test plugin custom configuration`() {
val pom = createTempFile().toFile()
pom.writeText(
"""
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.cqfn.diktat</groupId>
<artifactId>diktat-test</artifactId>
<version>1.0.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.cqfn.diktat</groupId>
<artifactId>diktat-maven-plugin</artifactId>
<configuration>
<diktatConfigFile>diktat-analysis.yml</diktatConfigFile>
<debug>true</debug>
<diktatConfigFile>my-diktat-config.yml</diktatConfigFile>
<inputs>
<input>${'$'}{project.basedir}/src/main/kotlin</input>
<input>${'$'}{project.basedir}/src/test/kotlin</input>
</inputs>
<excludes>
<exclude>${'$'}{project.basedir}/src/main/kotlin/exclusion</exclude>
</excludes>
</configuration>
<executions>
<execution>
Expand All @@ -54,8 +121,23 @@ class DiktatBaseMojoTest {
</project>
""".trimIndent()
)
val diktatCheckMojo = mojoRule.lookupMojo("check", pom.toFile()) as DiktatCheckMojo
Assert.assertEquals(false, diktatCheckMojo.debug)
Assert.assertEquals("diktat-analysis.yml", diktatCheckMojo.diktatConfigFile)
val mavenProject = projectBuilder.build(pom, buildingRequest).project

val diktatCheckMojo = mojoRule.lookupConfiguredMojo(mavenProject, "check") as DiktatCheckMojo
Assertions.assertTrue(diktatCheckMojo.debug)
Assertions.assertEquals("my-diktat-config.yml", diktatCheckMojo.diktatConfigFile)
Assertions.assertIterableEquals(
listOf(pom.parentFile.toPath() / "src/main/kotlin", pom.parentFile.toPath() / "src/test/kotlin"),
diktatCheckMojo.inputs.map { Path(it) }
)
Assertions.assertIterableEquals(
listOf(pom.parentFile.toPath() / "src/main/kotlin/exclusion"),
diktatCheckMojo.excludes.map { Path(it) }
)
val mojoExecutionException = Assertions.assertThrows(MojoExecutionException::class.java) {
diktatCheckMojo.execute()
}
Assertions.assertEquals("Configuration file my-diktat-config.yml doesn't exist",
mojoExecutionException.message)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,24 @@ import com.soebes.itf.jupiter.extension.MavenJupiterExtension
import com.soebes.itf.jupiter.extension.MavenTest
import com.soebes.itf.jupiter.maven.MavenExecutionResult
import org.junit.jupiter.api.Assertions
import java.io.File
import kotlin.io.path.ExperimentalPathApi
import kotlin.io.path.readText

/**
* Integration tests for diktat-maven-plugin.
* Run against the project from diktat-examples.
* Note: for maven itf test name should equal example project's directory name, which we have in pom.xml.
* Integration tests for diktat-maven-plugin. Run against the project from diktat-examples.
* The whole pipeline is as follows:
* * For each test case, test data is copied from examples with respect to maven-itf requirements, .mvn/jvm.config is copied too
* Note: for maven itf test name should equal example project's directory name, which we have in pom.xml.
* * maven-failsafe-plugin launches tests; for each test case a separate maven process is spawned
* * maven execution results are analyzed here; .mvn/jvm.config is used to attach jacoco java agent to every maven process and generate individual execution reports
*/
@OptIn(ExperimentalPathApi::class)
@MavenJupiterExtension
class DiktatMavenPluginIntegrationTest {
@MavenTest
@MavenGoal("diktat:check@diktat")
fun maven(result: MavenExecutionResult) {
fun diktatCheck(result: MavenExecutionResult) {
Assertions.assertEquals(1, result.returnCode)
Assertions.assertFalse(result.isError)
Assertions.assertFalse(result.isSuccesful)
Expand All @@ -28,5 +32,33 @@ class DiktatMavenPluginIntegrationTest {
Assertions.assertTrue(
mavenLog.contains("[HEADER_MISSING_OR_WRONG_COPYRIGHT]")
)

File(result.mavenProjectResult.baseDir, "target/jacoco-it.exec").copyTo(
File("target/jacoco-it-1.exec")
)
}

@MavenTest
@MavenGoal("diktat:fix@diktat")
fun diktatFix(result: MavenExecutionResult) {
Assertions.assertEquals(1, result.returnCode)
Assertions.assertFalse(result.isError)
Assertions.assertFalse(result.isSuccesful)
Assertions.assertTrue(result.isFailure)

val mavenLog = result.mavenLog.stdout.readText()
Assertions.assertTrue(
mavenLog.contains("Original and formatted content differ, writing to Test.kt...")
)
Assertions.assertTrue(
mavenLog.contains(Regex("There are \\d+ lint errors"))
)
Assertions.assertTrue(
mavenLog.contains("[MISSING_KDOC_TOP_LEVEL]")
)

File(result.mavenProjectResult.baseDir, "target/jacoco-it.exec").copyTo(
File("target/jacoco-it-2.exec")
)
}
}
1 change: 1 addition & 0 deletions diktat-maven-plugin/src/test/resources/.mvn/jvm.config
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/${jacoco.version}/org.jacoco.agent-${jacoco.version}-runtime.jar=destfile=target/jacoco-it.exec,includes=org.cqfn.diktat.plugin.maven.*
2 changes: 1 addition & 1 deletion examples/gradle-groovy-dsl/src/main/kotlin/Test.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package incorrect

class incorrectname: Exception {
class incorrectname: Exception() {
fun INCORRECT_FUNCTION() {
throw Exception()
}
Expand Down
2 changes: 1 addition & 1 deletion examples/gradle-kotlin-dsl/src/main/kotlin/Test.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package incorrect

class incorrectname: Exception {
class incorrectname: Exception() {
fun INCORRECT_FUNCTION() {
throw Exception()
}
Expand Down
2 changes: 1 addition & 1 deletion examples/maven/src/main/kotlin/Test.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package incorrect

class incorrectname: Exception {
class incorrectname: Exception() {
fun INCORRECT_FUNCTION() {
throw Exception()
}
Expand Down

0 comments on commit e71d22e

Please sign in to comment.