From 35753dd7444616da480f83fcfa6bb4e5d1e29596 Mon Sep 17 00:00:00 2001 From: Andrey Shcheglov Date: Thu, 26 May 2022 12:26:28 +0300 Subject: [PATCH 1/4] Refactor `DiktatMavenPluginIntegrationTest` to ease troubleshooting in case of test failures ### What's done: * `TestInfo` is now passed to test methods so that `jacoco-it-*.exec` files are unique. * `jacoco-it-*.exec` files are now deleted before each test, so repeated test invocations no longer cause failures. * Added `String.assertContains()` extensions so that assertion failures have meaningful messages. * Proofread and expanded the KDoc comment. --- .../maven/DiktatMavenPluginIntegrationTest.kt | 85 ++++++++++++++----- 1 file changed, 65 insertions(+), 20 deletions(-) diff --git a/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest.kt b/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest.kt index dd44337614..e3d39128e9 100644 --- a/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest.kt +++ b/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest.kt @@ -5,60 +5,105 @@ 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 org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.TestInfo import java.io.File import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.Path +import kotlin.io.path.deleteIfExists +import kotlin.io.path.div import kotlin.io.path.readText /** * 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 and .mvn/maven.config are 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. diktat.version is taken from .mvn/maven.config + * * For each test case, test data is copied from examples with respect to maven-itf requirements, .mvn/jvm.config and .mvn/maven.config are copied, too. + * **Note**: for maven its 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. `diktat.version` is taken from `.mvn/maven.config` * and the exact value is written when maven copies resources. - * * 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 + * * 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. + * + * When within an IDE (e.g.: _IDEA_), don't run this test directly: it's + * expected to be executed from a forked JVM. Instead, run a `verify` _lifecycle + * phase_ for the `diktat-maven-plugin` submodule, as if you were running + * + * ```console + * $ mvn -pl diktat-maven-plugin verify + * ``` + * + * from your terminal. If multiple JDKs are installed, be sure to pass + * `JAVA_HOME` to the _run configuration_, so that the parent and the forked + * JVMs have the same version. */ @OptIn(ExperimentalPathApi::class) @MavenJupiterExtension class DiktatMavenPluginIntegrationTest { + @BeforeEach + fun beforeEach(testInfo: TestInfo) { + val method = testInfo.testMethod.orElse(null) ?: return + + (Path("target") / "jacoco-it-${method.name}.exec").deleteIfExists() + } + @MavenTest @MavenGoal("diktat:check@diktat") - fun diktatCheck(result: MavenExecutionResult) { + fun diktatCheck(testInfo: TestInfo, result: MavenExecutionResult) { Assertions.assertEquals(1, result.returnCode) Assertions.assertFalse(result.isSuccessful) Assertions.assertTrue(result.isFailure) val mavenLog = result.mavenLog.stdout.readText() - Assertions.assertTrue( - mavenLog.contains("[FILE_NAME_MATCH_CLASS]") - ) + mavenLog.assertContains("[FILE_NAME_MATCH_CLASS]") + + val method = testInfo.testMethod.get() File(result.mavenProjectResult.targetProjectDirectory, "target/jacoco-it.exec").copyTo( - File("target/jacoco-it-1.exec") + File("target/jacoco-it-${method.name}.exec") ) } @MavenTest @MavenGoal("diktat:fix@diktat") - fun diktatFix(result: MavenExecutionResult) { + fun diktatFix(testInfo: TestInfo, result: MavenExecutionResult) { Assertions.assertEquals(1, result.returnCode) Assertions.assertFalse(result.isSuccessful) Assertions.assertTrue(result.isFailure) val mavenLog = result.mavenLog.stdout.readText() - Assertions.assertTrue( - mavenLog.contains(Regex("""Original and formatted content differ, writing to [:\w/\\]+Test\.kt\.\.\.""")) - ) - Assertions.assertTrue( - mavenLog.contains(Regex("There are \\d+ lint errors")) - ) - Assertions.assertTrue( - mavenLog.contains("[MISSING_KDOC_TOP_LEVEL]") - ) + mavenLog.assertContains(Regex("""Original and formatted content differ, writing to [:\w/\\]+Test\.kt\.\.\.""")) + mavenLog.assertContains(Regex("There are \\d+ lint errors")) + mavenLog.assertContains("[MISSING_KDOC_TOP_LEVEL]") + val method = testInfo.testMethod.get() File(result.mavenProjectResult.targetProjectDirectory, "target/jacoco-it.exec").copyTo( - File("target/jacoco-it-2.exec") + File("target/jacoco-it-${method.name}.exec") ) } + + /** + * Asserts that this string contains a [substring][other]. + * + * @param other the expected substring. + */ + private inline fun String.assertContains(other: CharSequence, + crossinline lazyMessage: () -> String = { + "The string: \"$this\" doesn't contain the substring: \"$other\"" + }) { + Assertions.assertTrue(contains(other)) { + lazyMessage() + } + } + + /** + * Asserts that this string contains a substring matching the [regex]. + */ + private inline fun String.assertContains(regex: Regex, + crossinline lazyMessage: () -> String = { + "The string: \"$this\" doesn't contain any substring matching the regex: \"$regex\"" + }) { + Assertions.assertTrue(contains(regex)) { + lazyMessage() + } + } } From cb82b14f947e5c8219d12f4b5c6f090cf1e5f52d Mon Sep 17 00:00:00 2001 From: Andrey ``Bass'' Shcheglov Date: Thu, 26 May 2022 12:50:26 +0300 Subject: [PATCH 2/4] Update diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest.kt Co-authored-by: Peter Trifanov --- .../diktat/plugin/maven/DiktatMavenPluginIntegrationTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest.kt b/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest.kt index e3d39128e9..35573f9d4e 100644 --- a/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest.kt +++ b/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest.kt @@ -18,7 +18,7 @@ import kotlin.io.path.readText * 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 and .mvn/maven.config are copied, too. - * **Note**: for maven its test name should equal example project's directory name, which we have in `pom.xml`. + * **Note**: for maven-itf-plugin, 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. `diktat.version` is taken from `.mvn/maven.config` * and the exact value is written when maven copies resources. * * 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. From 0ee5846cc1500535a666e882afff3cf892a98969 Mon Sep 17 00:00:00 2001 From: Andrey Shcheglov Date: Thu, 26 May 2022 13:32:53 +0300 Subject: [PATCH 3/4] Fix the indentation ### What's done: * Indentation fixed with diktat:fix@diktat --- .../plugin/maven/DiktatMavenPluginIntegrationTest.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest.kt b/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest.kt index 35573f9d4e..31b8e8089f 100644 --- a/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest.kt +++ b/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest.kt @@ -88,8 +88,8 @@ class DiktatMavenPluginIntegrationTest { */ private inline fun String.assertContains(other: CharSequence, crossinline lazyMessage: () -> String = { - "The string: \"$this\" doesn't contain the substring: \"$other\"" - }) { + "The string: \"$this\" doesn't contain the substring: \"$other\"" + }) { Assertions.assertTrue(contains(other)) { lazyMessage() } @@ -100,8 +100,8 @@ class DiktatMavenPluginIntegrationTest { */ private inline fun String.assertContains(regex: Regex, crossinline lazyMessage: () -> String = { - "The string: \"$this\" doesn't contain any substring matching the regex: \"$regex\"" - }) { + "The string: \"$this\" doesn't contain any substring matching the regex: \"$regex\"" + }) { Assertions.assertTrue(contains(regex)) { lazyMessage() } From daed53d923b49559ea9e1054d7cf83a3bc2aff1f Mon Sep 17 00:00:00 2001 From: Andrey Shcheglov Date: Thu, 26 May 2022 13:48:04 +0300 Subject: [PATCH 4/4] Switch to AssertJ ### What's done: * Switched to AssertJ and dropped the extensions --- .../maven/DiktatMavenPluginIntegrationTest.kt | 42 ++++++------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest.kt b/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest.kt index 31b8e8089f..493776dde8 100644 --- a/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest.kt +++ b/diktat-maven-plugin/src/test/kotlin/org/cqfn/diktat/plugin/maven/DiktatMavenPluginIntegrationTest.kt @@ -4,6 +4,8 @@ import com.soebes.itf.jupiter.extension.MavenGoal import com.soebes.itf.jupiter.extension.MavenJupiterExtension import com.soebes.itf.jupiter.extension.MavenTest import com.soebes.itf.jupiter.maven.MavenExecutionResult +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.SoftAssertions import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.TestInfo @@ -54,7 +56,7 @@ class DiktatMavenPluginIntegrationTest { val mavenLog = result.mavenLog.stdout.readText() - mavenLog.assertContains("[FILE_NAME_MATCH_CLASS]") + assertThat(mavenLog).contains("[FILE_NAME_MATCH_CLASS]") val method = testInfo.testMethod.get() File(result.mavenProjectResult.targetProjectDirectory, "target/jacoco-it.exec").copyTo( @@ -71,39 +73,19 @@ class DiktatMavenPluginIntegrationTest { val mavenLog = result.mavenLog.stdout.readText() - mavenLog.assertContains(Regex("""Original and formatted content differ, writing to [:\w/\\]+Test\.kt\.\.\.""")) - mavenLog.assertContains(Regex("There are \\d+ lint errors")) - mavenLog.assertContains("[MISSING_KDOC_TOP_LEVEL]") + with(SoftAssertions()) { + try { + assertThat(mavenLog).containsPattern("""Original and formatted content differ, writing to [:\w/\\]+Test\.kt\.\.\.""") + assertThat(mavenLog).containsPattern("There are \\d+ lint errors") + assertThat(mavenLog).contains("[MISSING_KDOC_TOP_LEVEL]") + } finally { + assertAll() + } + } val method = testInfo.testMethod.get() File(result.mavenProjectResult.targetProjectDirectory, "target/jacoco-it.exec").copyTo( File("target/jacoco-it-${method.name}.exec") ) } - - /** - * Asserts that this string contains a [substring][other]. - * - * @param other the expected substring. - */ - private inline fun String.assertContains(other: CharSequence, - crossinline lazyMessage: () -> String = { - "The string: \"$this\" doesn't contain the substring: \"$other\"" - }) { - Assertions.assertTrue(contains(other)) { - lazyMessage() - } - } - - /** - * Asserts that this string contains a substring matching the [regex]. - */ - private inline fun String.assertContains(regex: Regex, - crossinline lazyMessage: () -> String = { - "The string: \"$this\" doesn't contain any substring matching the regex: \"$regex\"" - }) { - Assertions.assertTrue(contains(regex)) { - lazyMessage() - } - } }