diff --git a/diktat-maven-plugin/pom.xml b/diktat-maven-plugin/pom.xml index 1172a07260..8f296767d5 100644 --- a/diktat-maven-plugin/pom.xml +++ b/diktat-maven-plugin/pom.xml @@ -142,37 +142,10 @@ org.apache.maven.plugins maven-surefire-plugin - - - me.fabriciorby - maven-surefire-junit5-tree-reporter - ${junit-tree-reporter.version} - - *IntegrationTest* - - - true - true - true - true - - - plain - - true - - - - true - true - - false - @@ -192,13 +165,6 @@ org.apache.maven.plugins maven-failsafe-plugin - - - me.fabriciorby - maven-surefire-junit5-tree-reporter - ${junit-tree-reporter.version} - - ${maven.version} @@ -207,28 +173,6 @@ **/*IntegrationTest* - ${project.build.testSourceDirectory} - ${project.build.testOutputDirectory} - - - true - true - true - true - - - plain - - true - - - - true - true - - false - diff --git a/diktat-rules/pom.xml b/diktat-rules/pom.xml index adf3254387..f9a42ec34e 100644 --- a/diktat-rules/pom.xml +++ b/diktat-rules/pom.xml @@ -12,11 +12,6 @@ 1.2.4-SNAPSHOT - - 1.8 - 1.8 - - @@ -165,144 +160,6 @@ org.apache.maven.plugins maven-surefire-plugin - - - me.fabriciorby - maven-surefire-junit5-tree-reporter - ${junit-tree-reporter.version} - - - - - **/DiktatSaveSmokeTest.* - - - - true - true - true - true - - - plain - - true - - - - true - true - - false - - - - - - org.apache.maven.plugins - maven-failsafe-plugin - - - **/DiktatSaveSmokeTest.* - - - - - - integration-test - verify - - - - - - - org.jacoco - jacoco-maven-plugin - - - - default-prepare-agent-integration - - prepare-agent-integration - - - - default-merge - post-integration-test - - merge - - - - - ${project.build.directory} - - jacoco.exec - jacoco-it.exec - - - - ${project.build.directory}/jacoco-merged.exec - - - - default-report-merged - - report-integration - - - ${project.reporting.outputDirectory}/jacoco - ${project.build.directory}/jacoco-merged.exec - - - - - - - - org.apache.maven.plugins - maven-shade-plugin - - - false - - false - - ${project.name}-${project.version}-fat-jar-for-smoke-tests - - - com.squareup:kotlinpoet - net.java.dev.jna:jna - org.jetbrains.intellij.deps:trove4j - org.jetbrains.kotlin:kotlin-compiler-embeddable - org.jetbrains.kotlin:kotlin-daemon-embeddable - org.jetbrains.kotlin:kotlin-reflect - org.jetbrains.kotlin:kotlin-script-runtime - org.jetbrains.kotlin:kotlin-stdlib-common - org.jetbrains.kotlin:kotlin-stdlib-jdk7 - org.jetbrains.kotlin:kotlin-stdlib-jdk8 - org.jetbrains.kotlin:kotlin-stdlib - org.jetbrains:annotations - - - - - - fat-jar-for-smoke-tests - package - - shade - - - diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/KtlintUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/KtlintUtils.kt index 3c2fc353f4..6d773bd279 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/KtlintUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/KtlintUtils.kt @@ -1,10 +1,27 @@ -@file:Suppress("HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE") +@file:Suppress( + "HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE", + "Deprecation", +) package org.cqfn.diktat.ruleset.utils +import org.cqfn.diktat.common.utils.loggerWithKtlintConfig import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.KtLint.ExperimentalParams import com.pinterest.ktlint.core.LintError +import com.pinterest.ktlint.core.RuleSetProvider +import mu.KotlinLogging +import org.intellij.lang.annotations.Language + +@Suppress("EMPTY_BLOCK_STRUCTURE_ERROR") +private val log = KotlinLogging.loggerWithKtlintConfig {} + +@Suppress("TYPE_ALIAS") +val defaultCallback: (lintError: LintError, corrected: Boolean) -> Unit = { lintError, _ -> + log.warn("Received linting error: $lintError") +} + +typealias LintErrorCallback = (LintError, Boolean) -> Unit /** * Enables ignoring autocorrected errors when in "fix" mode (i.e. when @@ -27,3 +44,30 @@ fun ExperimentalParams.ignoreCorrectedErrors(): ExperimentalParams = cb(error, false) } }) + +/** + * @param ruleSetProviderRef + * @param text + * @param fileName + * @param cb callback to be called on unhandled [LintError]s + * @return formatted code + */ +@Suppress("LAMBDA_IS_NOT_LAST_PARAMETER") +fun format( + ruleSetProviderRef: () -> RuleSetProvider, + @Language("kotlin") text: String, + fileName: String, + cb: LintErrorCallback = defaultCallback +): String { + val ruleSets = listOf(ruleSetProviderRef().get()) + return KtLint.format( + ExperimentalParams( + text = text, + ruleSets = ruleSets, + fileName = fileName.removeSuffix("_copy"), + script = fileName.removeSuffix("_copy").endsWith("kts"), + cb = cb, + debug = true, + ).ignoreCorrectedErrors() + ) +} diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/indentation/IndentationConfig.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/indentation/IndentationConfig.kt index ac063c352d..fc416e69c5 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/indentation/IndentationConfig.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/indentation/IndentationConfig.kt @@ -5,7 +5,7 @@ import org.cqfn.diktat.common.config.rules.RuleConfiguration /** * [RuleConfiguration] for indentation logic */ -internal class IndentationConfig(config: Map) : RuleConfiguration(config) { +class IndentationConfig(config: Map) : RuleConfiguration(config) { /** * Is newline at the end of a file needed */ @@ -87,7 +87,7 @@ internal class IndentationConfig(config: Map) : RuleConfiguratio override fun toString(): String = "${javaClass.simpleName}$configWithExplicitDefaults" - internal companion object { + companion object { internal const val ALIGNED_PARAMETERS = "alignedParameters" /** @@ -95,12 +95,12 @@ internal class IndentationConfig(config: Map) : RuleConfiguratio * `indentationSize`. */ private const val DEFAULT_INDENTATION_SIZE = 4 - internal const val EXTENDED_INDENT_AFTER_OPERATORS = "extendedIndentAfterOperators" - internal const val EXTENDED_INDENT_BEFORE_DOT = "extendedIndentBeforeDot" - internal const val EXTENDED_INDENT_FOR_EXPRESSION_BODIES = "extendedIndentForExpressionBodies" - internal const val EXTENDED_INDENT_OF_PARAMETERS = "extendedIndentOfParameters" - internal const val INDENTATION_SIZE = "indentationSize" - internal const val NEWLINE_AT_END = "newlineAtEnd" + const val EXTENDED_INDENT_AFTER_OPERATORS = "extendedIndentAfterOperators" + const val EXTENDED_INDENT_BEFORE_DOT = "extendedIndentBeforeDot" + const val EXTENDED_INDENT_FOR_EXPRESSION_BODIES = "extendedIndentForExpressionBodies" + const val EXTENDED_INDENT_OF_PARAMETERS = "extendedIndentOfParameters" + const val INDENTATION_SIZE = "indentationSize" + const val NEWLINE_AT_END = "newlineAtEnd" @Suppress("CUSTOM_GETTERS_SETTERS") private val IndentationConfig.configWithExplicitDefaults: Map diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt deleted file mode 100644 index c69e6bb20b..0000000000 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt +++ /dev/null @@ -1,123 +0,0 @@ -package org.cqfn.diktat.ruleset.smoke - -import org.cqfn.diktat.common.config.rules.DIKTAT_COMMON -import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID -import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_NO_EMPTY_TAGS -import org.cqfn.diktat.ruleset.constants.Warnings.MISSING_KDOC_CLASS_ELEMENTS -import org.cqfn.diktat.ruleset.constants.Warnings.MISSING_KDOC_ON_FUNCTION -import org.cqfn.diktat.ruleset.constants.Warnings.MISSING_KDOC_TOP_LEVEL -import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_INDENTATION -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider -import org.cqfn.diktat.ruleset.rules.chapter2.comments.CommentsRule -import org.cqfn.diktat.ruleset.rules.chapter2.kdoc.KdocComments -import org.cqfn.diktat.ruleset.rules.chapter2.kdoc.KdocFormatting -import org.cqfn.diktat.ruleset.rules.chapter2.kdoc.KdocMethods -import org.cqfn.diktat.ruleset.utils.indentation.IndentationConfig.Companion.NEWLINE_AT_END -import org.cqfn.diktat.util.deleteIfExistsSilently - -import com.pinterest.ktlint.core.LintError -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Tag -import org.junit.jupiter.api.Test - -import java.io.File - -/** - * Test for [DiktatRuleSetProvider] in autocorrect mode as a whole. All rules are applied to a file. - * Note: ktlint uses initial text from a file to calculate line and column from offset. Because of that line/col of unfixed errors - * may change after some changes to text or other rules. - */ -class DiktatSmokeTest : DiktatSmokeTestBase() { - override val isLintErrors = true - override fun fixAndCompare( - config: String, - expected: String, - test: String, - ) { - fixAndCompareSmokeTest(expected, test) - } - - @Test - @Tag("DiktatRuleSetProvider") - fun `smoke test with multiplatform project layout`() { - fixAndCompareSmokeTest("../../jsMain/kotlin/org/cqfn/diktat/scripts/ScriptExpected.kt", - "../../jsMain/kotlin/org/cqfn/diktat/scripts/ScriptTest.kt") - } - - @Test - @Tag("DiktatRuleSetProvider") - fun `smoke test with kts files`() { - overrideRulesConfig( - emptyList(), - mapOf( - WRONG_INDENTATION.name to mapOf( - NEWLINE_AT_END to "false", - ) - ) - ) // so that trailing newline isn't checked, because it's incorrectly read in tests and we are comparing file with itself - // file name is `gradle_` so that IDE doesn't suggest to import gradle project - val tmpTestFile = javaClass.classLoader - .getResource("$resourceFilePath/../../../build.gradle_.kts")!! - .toURI() - .let { - val tmpTestFile = File(it).parentFile.resolve("build.gradle.kts") - File(it).copyTo(tmpTestFile) - tmpTestFile - } - try { - val tmpFilePath = "../../../build.gradle.kts" - fixAndCompare(tmpFilePath, tmpFilePath) - Assertions.assertTrue(unfixedLintErrors.isEmpty()) - } finally { - tmpTestFile.toPath().deleteIfExistsSilently() - } - } - - @Test - @Tag("DiktatRuleSetProvider") - fun `smoke test with gradle script plugin`() { - fixAndCompareSmokeTest("kotlin-library-expected.gradle.kts", "kotlin-library.gradle.kts") - assertThat(unfixedLintErrors).containsExactly( - LintError(2, 1, "$DIKTAT_RULE_SET_ID:${CommentsRule.NAME_ID}", "[COMMENTED_OUT_CODE] you should not comment out code, " + - "use VCS to save it in history and delete this block: import org.jetbrains.kotlin.gradle.dsl.jvm", false) - ) - } - - @Test - @Tag("DiktatRuleSetProvider") - fun `disable chapters`() { - overrideRulesConfig( - emptyList(), - mapOf( - DIKTAT_COMMON to mapOf( - "domainName" to "org.cqfn.diktat", - "disabledChapters" to "Naming,3,4,5,Classes" - ) - ) - ) - fixAndCompareSmokeTest("Example1-2Expected.kt", "Example1Test.kt") - assertThat(unfixedLintErrors).containsExactlyInAnyOrder( - LintError(1, 1, "$DIKTAT_RULE_SET_ID:${KdocFormatting.NAME_ID}", "${KDOC_NO_EMPTY_TAGS.warnText()} @return", false), - LintError(3, 1, "$DIKTAT_RULE_SET_ID:${KdocComments.NAME_ID}", "${MISSING_KDOC_TOP_LEVEL.warnText()} example", false), - LintError(3, 16, "$DIKTAT_RULE_SET_ID:${KdocComments.NAME_ID}", "${MISSING_KDOC_CLASS_ELEMENTS.warnText()} isValid", false), - LintError(6, 5, "$DIKTAT_RULE_SET_ID:${KdocComments.NAME_ID}", "${MISSING_KDOC_CLASS_ELEMENTS.warnText()} foo", false), - LintError(6, 5, "$DIKTAT_RULE_SET_ID:${KdocMethods.NAME_ID}", "${MISSING_KDOC_ON_FUNCTION.warnText()} foo", false), - LintError(8, 5, "$DIKTAT_RULE_SET_ID:${KdocComments.NAME_ID}", "${MISSING_KDOC_CLASS_ELEMENTS.warnText()} foo", false), - LintError(10, 4, "$DIKTAT_RULE_SET_ID:${KdocFormatting.NAME_ID}", "${KDOC_NO_EMPTY_TAGS.warnText()} @return", false), - LintError(13, 9, "$DIKTAT_RULE_SET_ID:${KdocFormatting.NAME_ID}", "${KDOC_NO_EMPTY_TAGS.warnText()} @return", false), - LintError(18, 40, "$DIKTAT_RULE_SET_ID:${KdocFormatting.NAME_ID}", "${KDOC_NO_EMPTY_TAGS.warnText()} @return", false) - ) - } - - /** - * @param expectedPath path to file with expected result, relative to [resourceFilePath] - * @param testPath path to file with code that will be transformed by formatter, relative to [resourceFilePath] - */ - private fun fixAndCompareSmokeTest(expectedPath: String, testPath: String) { - Assertions.assertTrue( - testComparatorUnit - .compareFilesFromResources(expectedPath, testPath, true) - ) - } -} diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/RulesConfigValidationTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/RulesConfigValidationTest.kt index 586eaf20d6..07e2135a97 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/RulesConfigValidationTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/RulesConfigValidationTest.kt @@ -1,7 +1,7 @@ package org.cqfn.diktat.ruleset.smoke import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider -import org.cqfn.diktat.util.deleteIfExistsSilently +import org.cqfn.diktat.test.framework.util.deleteIfExistsSilently import com.charleskorn.kaml.InvalidPropertyValueException import org.junit.jupiter.api.AfterEach diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt index c30dcc8ad2..925c531ae8 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/FixTestBase.kt @@ -5,94 +5,66 @@ package org.cqfn.diktat.util import org.cqfn.diktat.common.config.rules.RulesConfig +import org.cqfn.diktat.ruleset.utils.LintErrorCallback +import org.cqfn.diktat.ruleset.utils.defaultCallback +import org.cqfn.diktat.ruleset.utils.format import org.cqfn.diktat.test.framework.processing.FileComparisonResult import org.cqfn.diktat.test.framework.processing.TestComparatorUnit import com.pinterest.ktlint.core.Rule -import com.pinterest.ktlint.core.RuleSetProvider import org.intellij.lang.annotations.Language import org.junit.jupiter.api.Assertions import java.nio.file.Path import kotlin.io.path.bufferedWriter import kotlin.io.path.div -const val SAVE_VERSION: String = "0.3.2" - /** - * @property resourceFilePath path to files which will be compared in tests + * Base class for FixTest */ open class FixTestBase( - protected val resourceFilePath: String, - private val ruleSetProviderRef: (rulesConfigList: List?) -> RuleSetProvider, - private val cb: LintErrorCallback = defaultCallback, - private val rulesConfigList: List? = null, + resourceFilePath: String, + ruleSupplier: (rulesConfigList: List) -> Rule, + defaultRulesConfigList: List? = null, + cb: LintErrorCallback = defaultCallback, ) { /** * testComparatorUnit */ - protected val testComparatorUnit = TestComparatorUnit(resourceFilePath) { text, fileName -> - format(ruleSetProviderRef, text, fileName, rulesConfigList, cb = cb) + private val testComparatorUnitSupplier = { overrideRulesConfigList: List? -> + TestComparatorUnit( + resourceFilePath = resourceFilePath, + function = { expectedText, testFilePath -> + format( + ruleSetProviderRef = { DiktatRuleSetProvider4Test(ruleSupplier, overrideRulesConfigList ?: defaultRulesConfigList) }, + text = expectedText, + fileName = testFilePath, + cb = cb, + ) + }, + ) } - constructor(resourceFilePath: String, - ruleSupplier: (rulesConfigList: List) -> Rule, - rulesConfigList: List? = null, - cb: LintErrorCallback = defaultCallback - ) : this( - resourceFilePath, - { overrideRulesConfigList -> DiktatRuleSetProvider4Test(ruleSupplier, overrideRulesConfigList) }, - cb, - rulesConfigList - ) - /** * @param expectedPath path to file with expected result, relative to [resourceFilePath] * @param testPath path to file with code that will be transformed by formatter, relative to [resourceFilePath] - * @param overrideRulesConfigList optional override to [rulesConfigList] + * @param overrideRulesConfigList optional override to [defaultRulesConfigList] + * @param trimLastEmptyLine whether the last (empty) line should be + * discarded when reading the content of [testPath]. * @see fixAndCompareContent */ protected fun fixAndCompare( expectedPath: String, testPath: String, - overrideRulesConfigList: List = emptyList() + overrideRulesConfigList: List? = null, + trimLastEmptyLine: Boolean = false, ) { - val testComparatorUnit = if (overrideRulesConfigList.isNotEmpty()) { - TestComparatorUnit(resourceFilePath) { text, fileName -> - format(ruleSetProviderRef, text, fileName, overrideRulesConfigList) - } - } else { - testComparatorUnit - } - + val testComparatorUnit = testComparatorUnitSupplier(overrideRulesConfigList) Assertions.assertTrue( testComparatorUnit - .compareFilesFromResources(expectedPath, testPath) + .compareFilesFromResources(expectedPath, testPath, trimLastEmptyLine) + .isSuccessful ) } - private fun getSaveForCurrentOs() = when { - System.getProperty("os.name").startsWith("Linux", ignoreCase = true) -> "save-$SAVE_VERSION-linuxX64.kexe" - System.getProperty("os.name").startsWith("Mac", ignoreCase = true) -> "save-$SAVE_VERSION-macosX64.kexe" - System.getProperty("os.name").startsWith("Windows", ignoreCase = true) -> "save-$SAVE_VERSION-mingwX64.exe" - else -> "" - } - - /** - * @param testPath path to file with code that will be transformed by formatter, relative to [resourceFilePath] - * @return ProcessBuilder - */ - protected fun createProcessBuilderWithCmd(testPath: String): ProcessBuilder { - val filesDir = "src/test/resources/test/smoke" - val savePath = "$filesDir/${getSaveForCurrentOs()}" - - val systemName = System.getProperty("os.name") - val result = when { - systemName.startsWith("Linux", ignoreCase = true) || systemName.startsWith("Mac", ignoreCase = true) -> - ProcessBuilder("sh", "-c", "chmod 777 $savePath ; ./$savePath $filesDir/src/main/kotlin $testPath --log all") - else -> ProcessBuilder(savePath, "$filesDir/src/main/kotlin", testPath, "--log", "all") - } - return result - } - /** * Unlike [fixAndCompare], this method doesn't perform any assertions. * @@ -123,10 +95,8 @@ open class FixTestBase( out.write(expectedContent) } - val testComparatorUnit = TestComparatorUnit(tempDir.toString()) { text, fileName -> - format(ruleSetProviderRef, text, fileName, overrideRulesConfigList ?: rulesConfigList, cb) - } - - return testComparatorUnit.compareFilesFromFileSystem(expected, actual) + val testComparatorUnit = testComparatorUnitSupplier(overrideRulesConfigList) + return testComparatorUnit + .compareFilesFromFileSystem(expected, actual, false) } } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt index bc9412e7a5..4f8eef4716 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt @@ -8,184 +8,19 @@ package org.cqfn.diktat.util -import org.cqfn.diktat.common.config.rules.RulesConfig -import org.cqfn.diktat.common.utils.loggerWithKtlintConfig import org.cqfn.diktat.ruleset.constants.EmitType -import org.cqfn.diktat.ruleset.utils.ignoreCorrectedErrors import com.pinterest.ktlint.core.KtLint -import com.pinterest.ktlint.core.LintError import com.pinterest.ktlint.core.Rule import com.pinterest.ktlint.core.RuleSet -import com.pinterest.ktlint.core.RuleSetProvider -import mu.KotlinLogging import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.fail -import org.intellij.lang.annotations.Language import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import java.io.File -import java.io.IOException -import java.nio.file.FileVisitResult -import java.nio.file.FileVisitResult.CONTINUE -import java.nio.file.Files -import java.nio.file.Files.walkFileTree -import java.nio.file.NoSuchFileException -import java.nio.file.Path -import java.nio.file.SimpleFileVisitor -import java.nio.file.attribute.BasicFileAttributes import java.util.concurrent.atomic.AtomicInteger -import kotlin.io.path.absolute -import kotlin.io.path.deleteExisting -import kotlin.io.path.deleteIfExists -import kotlin.io.path.isDirectory -import kotlin.io.path.isSameFileAs internal const val TEST_FILE_NAME = "TestFileName.kt" -@Suppress("EMPTY_BLOCK_STRUCTURE_ERROR") -private val log = KotlinLogging.loggerWithKtlintConfig {} - -@Suppress("TYPE_ALIAS") -internal val defaultCallback: (lintError: LintError, corrected: Boolean) -> Unit = { lintError, _ -> - log.warn("Received linting error: $lintError") -} - -typealias LintErrorCallback = (LintError, Boolean) -> Unit - -/** - * Deletes the file if it exists, retrying as necessary if the file is - * blocked by another process (on Windows). - * - * @receiver the file or empty directory. - * @see Path.deleteIfExists - */ -@Suppress( - "EMPTY_BLOCK_STRUCTURE_ERROR", - "MAGIC_NUMBER", -) -internal fun Path.deleteIfExistsSilently() { - val attempts = 10 - - val deleted = retry(attempts, delayMillis = 100L, lazyDefault = { false }) { - deleteIfExists() - - /* - * Ignore the return code of `deleteIfExists()` (will be `false` - * if the file doesn't exist). - */ - true - } - - if (!deleted) { - log.warn { - "File \"${absolute()}\" not deleted after $attempts attempt(s)." - } - } -} - -/** - * Deletes this directory recursively. - * - * @see Path.deleteIfExistsRecursively - */ -internal fun Path.deleteRecursively() { - walkFileTree(this, object : SimpleFileVisitor() { - override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult { - file.deleteIfExistsSilently() - return CONTINUE - } - - override fun postVisitDirectory(dir: Path, exc: IOException?): FileVisitResult { - dir.deleteExisting() - return CONTINUE - } - }) -} - -/** - * Deletes this directory recursively if it exists. - * - * @return `true` if the existing directory was successfully deleted, `false` if - * the directory doesn't exist. - * @see Files.deleteIfExists - * @see Path.deleteRecursively - */ -@Suppress("FUNCTION_BOOLEAN_PREFIX") -internal fun Path.deleteIfExistsRecursively(): Boolean = - try { - deleteRecursively() - true - } catch (_: NoSuchFileException) { - false - } - -/** - * @receiver the 1st operand. - * @param other the 2nd operand. - * @return `true` if, and only if, the two paths locate the same `JAVA_HOME`. - */ -internal fun Path.isSameJavaHomeAs(other: Path): Boolean = - isDirectory() && - (isSameFileAsSafe(other) || - resolve("jre").isSameFileAsSafe(other) || - other.resolve("jre").isSameFileAsSafe(this)) - -/** - * The same as [Path.isSameFileAs], but doesn't throw any [NoSuchFileException] - * if either of the operands doesn't exist. - * - * @receiver the 1st operand. - * @param other the 2nd operand. - * @return `true` if, and only if, the two paths locate the same file. - * @see Path.isSameFileAs - */ -internal fun Path.isSameFileAsSafe(other: Path): Boolean = - try { - isSameFileAs(other) - } catch (_: NoSuchFileException) { - false - } - -/** - * Prepends the `PATH` of this process builder with [pathEntry]. - * - * @param pathEntry the entry to be prepended to the `PATH`. - */ -internal fun ProcessBuilder.prependPath(pathEntry: Path) { - require(pathEntry.isDirectory()) { - "$pathEntry is not a directory" - } - - val environment = environment() - - val defaultPathKey = "PATH" - val defaultWindowsPathKey = "Path" - - val pathKey = when { - /*- - * Keys of the Windows environment are case-insensitive ("PATH" == "Path"). - * Keys of the Java interface to the environment are not ("PATH" != "Path"). - * This is an attempt to work around the inconsistency. - */ - System.getProperty("os.name").startsWith("Windows") -> environment.keys.firstOrNull { key -> - key.equals(defaultPathKey, ignoreCase = true) - } ?: defaultWindowsPathKey - - else -> defaultPathKey - } - - val pathSeparator = File.pathSeparatorChar - val oldPath = environment[pathKey] - - val newPath = when { - oldPath.isNullOrEmpty() -> pathEntry.toString() - else -> "$pathEntry$pathSeparator$oldPath" - } - - environment[pathKey] = newPath -} - /** * Casts a nullable value to a non-`null` one, similarly to the `!!` * operator. @@ -196,34 +31,6 @@ internal fun ProcessBuilder.prependPath(pathEntry: Path) { internal fun T?.assertNotNull(lazyFailureMessage: () -> String = { "Expecting actual not to be null" }): T = this ?: fail(lazyFailureMessage()) -/** - * @param ruleSetProviderRef - * @param text - * @param fileName - * @param rulesConfigList - * @param cb callback to be called on unhandled [LintError]s - * @return formatted code - */ -@Suppress("LAMBDA_IS_NOT_LAST_PARAMETER") -internal fun format(ruleSetProviderRef: (rulesConfigList: List?) -> RuleSetProvider, - @Language("kotlin") text: String, - fileName: String, - rulesConfigList: List? = null, - cb: LintErrorCallback = defaultCallback -): String { - val ruleSets = listOf(ruleSetProviderRef.invoke(rulesConfigList).get()) - return KtLint.format( - KtLint.ExperimentalParams( - text = text, - ruleSets = ruleSets, - fileName = fileName.removeSuffix("_copy"), - script = fileName.removeSuffix("_copy").endsWith("kts"), - cb = cb, - debug = true, - ).ignoreCorrectedErrors() - ) -} - /** * This utility function lets you run arbitrary code on every node of given [code]. * It also provides you with counter which can be incremented inside [applyToNode] and then will be compared to [expectedAsserts]. @@ -260,42 +67,3 @@ internal fun applyToCode(code: String, .`as`("Number of expected asserts") .isEqualTo(expectedAsserts) } - -/** - * Retries the execution of the [block]. - * - * @param attempts the number of attempts (must be positive). - * @param delayMillis the timeout (in milliseconds) between the consecutive - * attempts. The default is 0. Ignored if [attempts] is 1. - * @param lazyDefault allows to override the return value if none of the - * attempts succeeds. By default, the last exception is thrown. - * @param block the block to execute. - * @return the result of the execution of the [block], or whatever [lazyDefault] - * evaluates to if none of the attempts is successful. - */ -internal fun retry( - attempts: Int, - delayMillis: Long = 0L, - lazyDefault: (Throwable) -> T = { error -> throw error }, - block: () -> T -): T { - require(attempts > 0) { - "The number of attempts should be positive: $attempts" - } - - var lastError: Throwable? = null - - for (i in 1..attempts) { - try { - return block() - } catch (error: Throwable) { - lastError = error - } - - if (delayMillis > 0L) { - Thread.sleep(delayMillis) - } - } - - return lazyDefault(lastError ?: Exception("The block was never executed")) -} diff --git a/diktat-ruleset/pom.xml b/diktat-ruleset/pom.xml index a7275178e1..9edd86b9b9 100644 --- a/diktat-ruleset/pom.xml +++ b/diktat-ruleset/pom.xml @@ -9,6 +9,7 @@ org.cqfn.diktat diktat-parent 1.2.4-SNAPSHOT + ../pom.xml @@ -36,10 +37,95 @@ + + + org.cqfn.diktat + diktat-test-framework + ${project.version} + test + + + org.jetbrains.kotlin + kotlin-stdlib-common + test + + + org.jetbrains.kotlin + kotlin-stdlib-jdk7 + test + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + test + + + org.jetbrains.kotlin + kotlin-stdlib + test + + + org.jetbrains.kotlin + kotlin-compiler-embeddable + test + + + org.apache.logging.log4j + log4j-core + test + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.junit.jupiter + junit-jupiter + test + + + org.junit.platform + junit-platform-suite-engine + test + + + org.assertj + assertj-core + test + + + org.mockito + mockito-all + test + + ${project.basedir}/src/test/kotlin + + org.jetbrains.kotlin + kotlin-maven-plugin + + + test-compile + process-test-sources + + test-compile + + + + + src/main/kotlin + src/test/kotlin + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + + + maven-jar-plugin @@ -76,6 +162,78 @@ + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/DiktatSaveSmokeTest.* + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + **/DiktatSaveSmokeTest.* + + + + + + integration-test + verify + + integration-test + + + + + + org.jacoco + jacoco-maven-plugin + + + + default-prepare-agent-integration + + prepare-agent-integration + + + + default-merge + post-integration-test + + merge + + + + + ${project.build.directory} + + jacoco.exec + jacoco-it.exec + + + + ${project.build.directory}/jacoco-merged.exec + + + + default-report-merged + + report-integration + + + ${project.reporting.outputDirectory}/jacoco + ${project.build.directory}/jacoco-merged.exec + + + + diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt similarity index 85% rename from diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt rename to diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt index 85c6f19bfb..912e358621 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt +++ b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt @@ -1,12 +1,12 @@ package org.cqfn.diktat.ruleset.smoke import org.cqfn.diktat.common.utils.loggerWithKtlintConfig -import org.cqfn.diktat.util.SAVE_VERSION -import org.cqfn.diktat.util.deleteIfExistsRecursively -import org.cqfn.diktat.util.deleteIfExistsSilently +import org.cqfn.diktat.test.framework.util.deleteIfExistsRecursively +import org.cqfn.diktat.test.framework.util.deleteIfExistsSilently +import org.cqfn.diktat.test.framework.util.retry import org.cqfn.diktat.util.isSameJavaHomeAs import org.cqfn.diktat.util.prependPath -import org.cqfn.diktat.util.retry +import com.pinterest.ktlint.core.LintError import mu.KotlinLogging import org.assertj.core.api.Assertions.assertThat @@ -34,15 +34,18 @@ import kotlin.system.measureNanoTime @DisabledOnOs(OS.MAC) class DiktatSaveSmokeTest : DiktatSmokeTestBase() { - override val isLintErrors = false override fun fixAndCompare( - config: String, + config: Path, expected: String, test: String, + trimLastEmptyLine: Boolean, ) { - saveSmokeTest(Path(config), test) + saveSmokeTest(config, test) } + // do nothing, we can't check unfixed lint errors here + override fun assertUnfixedLintErrors(lintErrorsConsumer: (List) -> Unit) = Unit + /** * @param testPath path to file with code that will be transformed by formatter, relative to [resourceFilePath] * @param configFilePath path of diktat-analysis file @@ -113,12 +116,30 @@ class DiktatSaveSmokeTest : DiktatSmokeTestBase() { } } + /** + * @param testPath path to file with code that will be transformed by formatter, relative to [resourceFilePath] + * @return ProcessBuilder + */ + private fun createProcessBuilderWithCmd(testPath: String): ProcessBuilder { + val filesDir = "src/test/resources/test/smoke" + val savePath = "$filesDir/${getSaveForCurrentOs()}" + + val systemName = System.getProperty("os.name") + val result = when { + systemName.startsWith("Linux", ignoreCase = true) || systemName.startsWith("Mac", ignoreCase = true) -> + ProcessBuilder("sh", "-c", "chmod 777 $savePath ; ./$savePath $filesDir/src/main/kotlin $testPath --log all") + else -> ProcessBuilder(savePath, "$filesDir/src/main/kotlin", testPath, "--log", "all") + } + return result + } + companion object { @Suppress("EMPTY_BLOCK_STRUCTURE_ERROR") private val logger = KotlinLogging.loggerWithKtlintConfig { } private const val BUILD_DIRECTORY = "target" - private const val FAT_JAR_GLOB = "diktat-rules-*-fat-jar-for-smoke-tests.jar" + private const val FAT_JAR_GLOB = "diktat-*.jar" private const val KTLINT_VERSION = "0.47.1" + private const val SAVE_VERSION: String = "0.3.4" private val baseDirectory = Path("src/test/resources/test/smoke").absolute() private fun getSaveForCurrentOs(): String { diff --git a/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt new file mode 100644 index 0000000000..46cf775a42 --- /dev/null +++ b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTest.kt @@ -0,0 +1,53 @@ +package org.cqfn.diktat.ruleset.smoke + +import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider +import org.cqfn.diktat.ruleset.utils.format +import org.cqfn.diktat.test.framework.processing.TestComparatorUnit +import com.pinterest.ktlint.core.LintError +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import java.nio.file.Path +import kotlin.io.path.absolutePathString + +/** + * Test for [DiktatRuleSetProvider] in autocorrect mode as a whole. All rules are applied to a file. + * Note: ktlint uses initial text from a file to calculate line and column from offset. Because of that line/col of unfixed errors + * may change after some changes to text or other rules. + */ +class DiktatSmokeTest : DiktatSmokeTestBase() { + private val unfixedLintErrors: MutableList = mutableListOf() + + override fun fixAndCompare( + config: Path, + expected: String, + test: String, + trimLastEmptyLine: Boolean, + ) { + Assertions.assertTrue( + getTestComparatorUnit(config) + .compareFilesFromResources(expected, test, trimLastEmptyLine) + .isSuccessful + ) + } + + @BeforeEach + internal fun setUp() { + unfixedLintErrors.clear() + } + + override fun assertUnfixedLintErrors(lintErrorsConsumer: (List) -> Unit) { + lintErrorsConsumer(unfixedLintErrors) + } + + private fun getTestComparatorUnit(config: Path) = TestComparatorUnit( + resourceFilePath = RESOURCE_FILE_PATH, + function = { expectedText, testFilePath -> + format( + ruleSetProviderRef = { DiktatRuleSetProvider(config.absolutePathString()) }, + text = expectedText, + fileName = testFilePath, + cb = { lintError, _ -> unfixedLintErrors.add(lintError) }, + ) + }, + ) +} diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTestBase.kt b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTestBase.kt similarity index 61% rename from diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTestBase.kt rename to diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTestBase.kt index 9bd9f22892..e7199ae6f2 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTestBase.kt +++ b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTestBase.kt @@ -20,7 +20,6 @@ import org.cqfn.diktat.ruleset.constants.Warnings.MISSING_KDOC_CLASS_ELEMENTS import org.cqfn.diktat.ruleset.constants.Warnings.MISSING_KDOC_ON_FUNCTION import org.cqfn.diktat.ruleset.constants.Warnings.MISSING_KDOC_TOP_LEVEL import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_INDENTATION -import org.cqfn.diktat.ruleset.rules.DiktatRuleSetProvider import org.cqfn.diktat.ruleset.rules.chapter1.FileNaming import org.cqfn.diktat.ruleset.rules.chapter2.comments.CommentsRule import org.cqfn.diktat.ruleset.rules.chapter2.comments.HeaderCommentRule @@ -29,24 +28,29 @@ import org.cqfn.diktat.ruleset.rules.chapter2.kdoc.KdocFormatting import org.cqfn.diktat.ruleset.rules.chapter2.kdoc.KdocMethods import org.cqfn.diktat.ruleset.rules.chapter3.EmptyBlock import org.cqfn.diktat.ruleset.rules.chapter6.classes.InlineClassesRule +import org.cqfn.diktat.ruleset.utils.indentation.IndentationConfig import org.cqfn.diktat.ruleset.utils.indentation.IndentationConfig.Companion.EXTENDED_INDENT_AFTER_OPERATORS import org.cqfn.diktat.ruleset.utils.indentation.IndentationConfig.Companion.EXTENDED_INDENT_BEFORE_DOT import org.cqfn.diktat.ruleset.utils.indentation.IndentationConfig.Companion.EXTENDED_INDENT_FOR_EXPRESSION_BODIES -import org.cqfn.diktat.util.FixTestBase +import org.cqfn.diktat.test.framework.util.deleteIfExistsSilently import com.charleskorn.kaml.Yaml import com.charleskorn.kaml.YamlConfiguration import com.pinterest.ktlint.core.LintError import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Tag import org.junit.jupiter.api.Test import org.junit.jupiter.api.Timeout +import java.io.File +import java.nio.file.Path import java.time.LocalDate import java.util.concurrent.TimeUnit.SECONDS +import kotlin.io.path.createTempFile +import kotlin.io.path.writeText import kotlinx.serialization.builtins.ListSerializer @@ -55,15 +59,7 @@ typealias RuleToConfig = Map> /** * Base class for smoke test classes */ -abstract class DiktatSmokeTestBase : FixTestBase("test/smoke/src/main/kotlin", - { DiktatRuleSetProvider(configFilePath) }, - { lintError, _ -> unfixedLintErrors.add(lintError) }, -) { - /** - * Flag to check LintError in smoke tests - */ - abstract val isLintErrors: Boolean - +abstract class DiktatSmokeTestBase { /** * Disable some of the rules. * @@ -71,7 +67,7 @@ abstract class DiktatSmokeTestBase : FixTestBase("test/smoke/src/main/kotlin", * @param rulesToOverride */ @Suppress("UnsafeCallOnNullableType") - open fun overrideRulesConfig(rulesToDisable: List, rulesToOverride: RuleToConfig = emptyMap()) { + private fun prepareOverriddenRulesConfig(rulesToDisable: List = emptyList(), rulesToOverride: RuleToConfig = emptyMap()): Path { val rulesConfig = RulesConfigReader(javaClass.classLoader).readResource(DEFAULT_CONFIG_PATH)!! .toMutableList() .also { rulesConfig -> @@ -85,32 +81,20 @@ abstract class DiktatSmokeTestBase : FixTestBase("test/smoke/src/main/kotlin", } } .toList() - kotlin.io.path.createTempFile() - .toFile() + return createTempFile() .also { - configFilePath = it.absolutePath + it.writeText( + Yaml(configuration = YamlConfiguration(strictMode = true)) + .encodeToString(ListSerializer(RulesConfig.serializer()), rulesConfig) + ) } - .writeText( - Yaml(configuration = YamlConfiguration(strictMode = true)) - .encodeToString(ListSerializer(RulesConfig.serializer()), rulesConfig) - ) - } - - @BeforeEach - fun setUp() { - unfixedLintErrors.clear() - } - - @AfterEach - fun tearDown() { - configFilePath = DEFAULT_CONFIG_PATH } @Test @Tag("DiktatRuleSetProvider") @Timeout(TEST_TIMEOUT_SECONDS, unit = SECONDS) fun `regression - should not fail if package is not set`() { - overrideRulesConfig(listOf(Warnings.PACKAGE_NAME_MISSING, Warnings.PACKAGE_NAME_INCORRECT_PATH, + val configFilePath = prepareOverriddenRulesConfig(listOf(Warnings.PACKAGE_NAME_MISSING, Warnings.PACKAGE_NAME_INCORRECT_PATH, Warnings.PACKAGE_NAME_INCORRECT_PREFIX)) fixAndCompare(configFilePath, "DefaultPackageExpected.kt", "DefaultPackageTest.kt") } @@ -119,21 +103,21 @@ abstract class DiktatSmokeTestBase : FixTestBase("test/smoke/src/main/kotlin", @Tag("DiktatRuleSetProvider") @Timeout(TEST_TIMEOUT_SECONDS, unit = SECONDS) fun `smoke test #8 - anonymous function`() { - fixAndCompare(configFilePath, "Example8Expected.kt", "Example8Test.kt") + fixAndCompare(prepareOverriddenRulesConfig(), "Example8Expected.kt", "Example8Test.kt") } @Test @Tag("DiktatRuleSetProvider") @Timeout(TEST_TIMEOUT_SECONDS, unit = SECONDS) fun `smoke test #7`() { - fixAndCompare(configFilePath, "Example7Expected.kt", "Example7Test.kt") + fixAndCompare(prepareOverriddenRulesConfig(), "Example7Expected.kt", "Example7Test.kt") } @Test @Tag("DiktatRuleSetProvider") @Timeout(TEST_TIMEOUT_SECONDS, unit = SECONDS) fun `smoke test #6`() { - overrideRulesConfig( + val configFilePath = prepareOverriddenRulesConfig( rulesToDisable = emptyList(), rulesToOverride = mapOf( WRONG_INDENTATION.name to mapOf( @@ -150,7 +134,7 @@ abstract class DiktatSmokeTestBase : FixTestBase("test/smoke/src/main/kotlin", @Tag("DiktatRuleSetProvider") @Timeout(TEST_TIMEOUT_SECONDS, unit = SECONDS) fun `smoke test #5`() { - overrideRulesConfig(emptyList(), + val configFilePath = prepareOverriddenRulesConfig(emptyList(), mapOf( Warnings.HEADER_MISSING_OR_WRONG_COPYRIGHT.name to mapOf( "isCopyrightMandatory" to "true", @@ -170,7 +154,7 @@ abstract class DiktatSmokeTestBase : FixTestBase("test/smoke/src/main/kotlin", ) fixAndCompare(configFilePath, "Example5Expected.kt", "Example5Test.kt") - if (isLintErrors) { + assertUnfixedLintErrors { unfixedLintErrors -> Assertions.assertFalse( unfixedLintErrors.contains(LintError(line = 1, col = 1, ruleId = "diktat-ruleset:${CommentsRule.NAME_ID}", detail = "${Warnings.COMMENTED_OUT_CODE.warnText()} /*")) ) @@ -185,28 +169,28 @@ abstract class DiktatSmokeTestBase : FixTestBase("test/smoke/src/main/kotlin", @Tag("DiktatRuleSetProvider") @Timeout(TEST_TIMEOUT_SECONDS, unit = SECONDS) fun `smoke test #4`() { - fixAndCompare(configFilePath, "Example4Expected.kt", "Example4Test.kt") + fixAndCompare(prepareOverriddenRulesConfig(), "Example4Expected.kt", "Example4Test.kt") } @Test @Tag("DiktatRuleSetProvider") @Timeout(TEST_TIMEOUT_SECONDS, unit = SECONDS) fun `smoke test #3`() { - fixAndCompare(configFilePath, "Example3Expected.kt", "Example3Test.kt") + fixAndCompare(prepareOverriddenRulesConfig(), "Example3Expected.kt", "Example3Test.kt") } @Test @Tag("DiktatRuleSetProvider") @Timeout(TEST_TIMEOUT_SECONDS, unit = SECONDS) fun `regression - shouldn't throw exception in cases similar to #371`() { - fixAndCompare(configFilePath, "Bug1Expected.kt", "Bug1Test.kt") + fixAndCompare(prepareOverriddenRulesConfig(), "Bug1Expected.kt", "Bug1Test.kt") } @Test @Tag("DiktatRuleSetProvider") @Timeout(TEST_TIMEOUT_SECONDS, unit = SECONDS) fun `smoke test #2`() { - overrideRulesConfig( + val configFilePath = prepareOverriddenRulesConfig( rulesToDisable = emptyList(), rulesToOverride = mapOf( WRONG_INDENTATION.name to mapOf( @@ -216,7 +200,7 @@ abstract class DiktatSmokeTestBase : FixTestBase("test/smoke/src/main/kotlin", ) ) fixAndCompare(configFilePath, "Example2Expected.kt", "Example2Test.kt") - if (isLintErrors) { + assertUnfixedLintErrors { unfixedLintErrors -> assertThat(unfixedLintErrors).containsExactlyInAnyOrder( LintError(1, 1, "$DIKTAT_RULE_SET_ID:${HeaderCommentRule.NAME_ID}", "${HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE.warnText()} there are 2 declared classes and/or objects", false), @@ -232,7 +216,7 @@ abstract class DiktatSmokeTestBase : FixTestBase("test/smoke/src/main/kotlin", @Tag("DiktatRuleSetProvider") @Timeout(TEST_TIMEOUT_SECONDS, unit = SECONDS) fun `smoke test #1`() { - overrideRulesConfig( + val configFilePath = prepareOverriddenRulesConfig( rulesToDisable = emptyList(), rulesToOverride = mapOf( WRONG_INDENTATION.name to mapOf( @@ -242,7 +226,7 @@ abstract class DiktatSmokeTestBase : FixTestBase("test/smoke/src/main/kotlin", ) ) fixAndCompare(configFilePath, "Example1Expected.kt", "Example1Test.kt") - if (isLintErrors) { + assertUnfixedLintErrors { unfixedLintErrors -> assertThat(unfixedLintErrors).containsExactlyInAnyOrder( LintError(1, 1, "$DIKTAT_RULE_SET_ID:${FileNaming.NAME_ID}", "${FILE_NAME_MATCH_CLASS.warnText()} Example1Test.kt vs Example", true), LintError(1, 1, "$DIKTAT_RULE_SET_ID:${KdocFormatting.NAME_ID}", "${KDOC_NO_EMPTY_TAGS.warnText()} @return", false), @@ -269,35 +253,35 @@ abstract class DiktatSmokeTestBase : FixTestBase("test/smoke/src/main/kotlin", @Tag("DiktatRuleSetProvider") @Timeout(TEST_TIMEOUT_SECONDS, unit = SECONDS) fun `smoke test with kts files #2`() { - fixAndCompare(configFilePath, "script/SimpleRunInScriptExpected.kts", "script/SimpleRunInScriptTest.kts") + fixAndCompare(prepareOverriddenRulesConfig(), "script/SimpleRunInScriptExpected.kts", "script/SimpleRunInScriptTest.kts") } @Test @Tag("DiktatRuleSetProvider") @Timeout(TEST_TIMEOUT_SECONDS, unit = SECONDS) fun `smoke test with kts files with package name`() { - fixAndCompare(configFilePath, "script/PackageInScriptExpected.kts", "script/PackageInScriptTest.kts") + fixAndCompare(prepareOverriddenRulesConfig(), "script/PackageInScriptExpected.kts", "script/PackageInScriptTest.kts") } @Test @Tag("DiktatRuleSetProvider") @Timeout(TEST_TIMEOUT_SECONDS, unit = SECONDS) fun `regression - should correctly handle tags with empty lines`() { - fixAndCompare(configFilePath, "KdocFormattingMultilineTagsExpected.kt", "KdocFormattingMultilineTagsTest.kt") + fixAndCompare(prepareOverriddenRulesConfig(), "KdocFormattingMultilineTagsExpected.kt", "KdocFormattingMultilineTagsTest.kt") } @Test @Tag("DiktatRuleSetProvider") @Timeout(TEST_TIMEOUT_SECONDS, unit = SECONDS) fun `regression - FP of local variables rule`() { - fixAndCompare(configFilePath, "LocalVariableWithOffsetExpected.kt", "LocalVariableWithOffsetTest.kt") + fixAndCompare(prepareOverriddenRulesConfig(), "LocalVariableWithOffsetExpected.kt", "LocalVariableWithOffsetTest.kt") } @Test @Tag("DiktatRuleSetProvider") @Timeout(TEST_TIMEOUT_SECONDS, unit = SECONDS) fun `fix can cause long line`() { - overrideRulesConfig( + val configFilePath = prepareOverriddenRulesConfig( rulesToDisable = emptyList(), rulesToOverride = mapOf( WRONG_INDENTATION.name to mapOf( @@ -308,18 +292,119 @@ abstract class DiktatSmokeTestBase : FixTestBase("test/smoke/src/main/kotlin", fixAndCompare(configFilePath, "ManyLineTransformInLongLineExpected.kt", "ManyLineTransformInLongLineTest.kt") } + @Test + @Tag("DiktatRuleSetProvider") + fun `smoke test with multiplatform project layout`() { + fixAndCompare( + prepareOverriddenRulesConfig(), + "../../jsMain/kotlin/org/cqfn/diktat/scripts/ScriptExpected.kt", + "../../jsMain/kotlin/org/cqfn/diktat/scripts/ScriptTest.kt" + ) + } + + @Test + @Tag("DiktatRuleSetProvider") + fun `smoke test with kts files`() { + val configFilePath = prepareOverriddenRulesConfig( + emptyList(), + mapOf( + WRONG_INDENTATION.name to mapOf( + IndentationConfig.NEWLINE_AT_END to "false", + ) + ) + ) // so that trailing newline isn't checked, because it's incorrectly read in tests and we are comparing file with itself + // file name is `gradle_` so that IDE doesn't suggest to import gradle project + val tmpFilePath = "../../../build.gradle.kts" + fixAndCompare(configFilePath, tmpFilePath, tmpFilePath, false) + assertUnfixedLintErrors { unfixedLintErrors -> + Assertions.assertTrue(unfixedLintErrors.isEmpty()) + } + } + + @Test + @Tag("DiktatRuleSetProvider") + fun `smoke test with gradle script plugin`() { + fixAndCompare(prepareOverriddenRulesConfig(), "kotlin-library-expected.gradle.kts", "kotlin-library.gradle.kts") + assertUnfixedLintErrors { unfixedLintErrors -> + assertThat(unfixedLintErrors).containsExactly( + LintError( + 2, 1, "$DIKTAT_RULE_SET_ID:${CommentsRule.NAME_ID}", "[COMMENTED_OUT_CODE] you should not comment out code, " + + "use VCS to save it in history and delete this block: import org.jetbrains.kotlin.gradle.dsl.jvm", false + ) + ) + } + } + + @Test + @Tag("DiktatRuleSetProvider") + fun `disable chapters`() { + val configFilePath = prepareOverriddenRulesConfig( + emptyList(), + mapOf( + DIKTAT_COMMON to mapOf( + "domainName" to "org.cqfn.diktat", + "disabledChapters" to "Naming,3,4,5,Classes" + ) + ) + ) + fixAndCompare(configFilePath, "Example1-2Expected.kt", "Example1-2Test.kt") + assertUnfixedLintErrors { unfixedLintErrors -> + assertThat(unfixedLintErrors).containsExactlyInAnyOrder( + LintError(1, 1, "$DIKTAT_RULE_SET_ID:${KdocFormatting.NAME_ID}", "${KDOC_NO_EMPTY_TAGS.warnText()} @return", false), + LintError(3, 1, "$DIKTAT_RULE_SET_ID:${KdocComments.NAME_ID}", "${MISSING_KDOC_TOP_LEVEL.warnText()} example", false), + LintError(3, 16, "$DIKTAT_RULE_SET_ID:${KdocComments.NAME_ID}", "${MISSING_KDOC_CLASS_ELEMENTS.warnText()} isValid", false), + LintError(6, 5, "$DIKTAT_RULE_SET_ID:${KdocComments.NAME_ID}", "${MISSING_KDOC_CLASS_ELEMENTS.warnText()} foo", false), + LintError(6, 5, "$DIKTAT_RULE_SET_ID:${KdocMethods.NAME_ID}", "${MISSING_KDOC_ON_FUNCTION.warnText()} foo", false), + LintError(8, 5, "$DIKTAT_RULE_SET_ID:${KdocComments.NAME_ID}", "${MISSING_KDOC_CLASS_ELEMENTS.warnText()} foo", false), + LintError(10, 4, "$DIKTAT_RULE_SET_ID:${KdocFormatting.NAME_ID}", "${KDOC_NO_EMPTY_TAGS.warnText()} @return", false), + LintError(13, 9, "$DIKTAT_RULE_SET_ID:${KdocFormatting.NAME_ID}", "${KDOC_NO_EMPTY_TAGS.warnText()} @return", false), + LintError(18, 40, "$DIKTAT_RULE_SET_ID:${KdocFormatting.NAME_ID}", "${KDOC_NO_EMPTY_TAGS.warnText()} @return", false) + ) + } + } + abstract fun fixAndCompare( - config: String, + config: Path, expected: String, test: String, + trimLastEmptyLine: Boolean = true, ) + abstract fun assertUnfixedLintErrors(lintErrorsConsumer: (List) -> Unit) + companion object { - const val DEFAULT_CONFIG_PATH = "../diktat-analysis.yml" + private const val DEFAULT_CONFIG_PATH = "../diktat-analysis.yml" + const val RESOURCE_FILE_PATH = "test/smoke/src/main/kotlin" private const val TEST_TIMEOUT_SECONDS = 25L - val unfixedLintErrors: MutableList = mutableListOf() + private val tmpFiles: MutableList = mutableListOf() - // by default using same yml config as for diktat code style check, but allow to override - var configFilePath = DEFAULT_CONFIG_PATH + @BeforeAll + @JvmStatic + @Suppress("AVOID_NULL_CHECKS") + internal fun createTmpFiles() { + listOf( + "$RESOURCE_FILE_PATH/../../../build.gradle_.kts" to "build.gradle.kts", + "$RESOURCE_FILE_PATH/Example1Test.kt" to "Example1-2Test.kt", + ) + .map { (resource, targetFileName) -> + DiktatSmokeTestBase::class.java + .classLoader + .getResource(resource)!! + .toURI() + .let { + val tmpTestFile = File(it).parentFile.resolve(targetFileName) + File(it).copyTo(tmpTestFile, true) + } + .let { tmpFiles.add(it) } + } + } + + @AfterAll + @JvmStatic + internal fun deleteTmpFiles() { + tmpFiles.forEach { + it.toPath().deleteIfExistsSilently() + } + } } } diff --git a/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/util/SmokeTestUtils.kt b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/util/SmokeTestUtils.kt new file mode 100644 index 0000000000..8b69310762 --- /dev/null +++ b/diktat-ruleset/src/test/kotlin/org/cqfn/diktat/util/SmokeTestUtils.kt @@ -0,0 +1,77 @@ +/** + * Utility classes and methods for tests + */ + +package org.cqfn.diktat.util + +import java.io.File +import java.nio.file.NoSuchFileException +import java.nio.file.Path +import kotlin.io.path.isDirectory +import kotlin.io.path.isSameFileAs + +/** + * @receiver the 1st operand. + * @param other the 2nd operand. + * @return `true` if, and only if, the two paths locate the same `JAVA_HOME`. + */ +internal fun Path.isSameJavaHomeAs(other: Path): Boolean = + isDirectory() && + (isSameFileAsSafe(other) || + resolve("jre").isSameFileAsSafe(other) || + other.resolve("jre").isSameFileAsSafe(this)) + +/** + * The same as [Path.isSameFileAs], but doesn't throw any [NoSuchFileException] + * if either of the operands doesn't exist. + * + * @receiver the 1st operand. + * @param other the 2nd operand. + * @return `true` if, and only if, the two paths locate the same file. + * @see Path.isSameFileAs + */ +internal fun Path.isSameFileAsSafe(other: Path): Boolean = + try { + isSameFileAs(other) + } catch (_: NoSuchFileException) { + false + } + +/** + * Prepends the `PATH` of this process builder with [pathEntry]. + * + * @param pathEntry the entry to be prepended to the `PATH`. + */ +internal fun ProcessBuilder.prependPath(pathEntry: Path) { + require(pathEntry.isDirectory()) { + "$pathEntry is not a directory" + } + + val environment = environment() + + val defaultPathKey = "PATH" + val defaultWindowsPathKey = "Path" + + val pathKey = when { + /*- + * Keys of the Windows environment are case-insensitive ("PATH" == "Path"). + * Keys of the Java interface to the environment are not ("PATH" != "Path"). + * This is an attempt to work around the inconsistency. + */ + System.getProperty("os.name").startsWith("Windows") -> environment.keys.firstOrNull { key -> + key.equals(defaultPathKey, ignoreCase = true) + } ?: defaultWindowsPathKey + + else -> defaultPathKey + } + + val pathSeparator = File.pathSeparatorChar + val oldPath = environment[pathKey] + + val newPath = when { + oldPath.isNullOrEmpty() -> pathEntry.toString() + else -> "$pathEntry$pathSeparator$oldPath" + } + + environment[pathKey] = newPath +} diff --git a/diktat-rules/src/test/resources/test/smoke/.gitignore b/diktat-ruleset/src/test/resources/test/smoke/.gitignore similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/.gitignore rename to diktat-ruleset/src/test/resources/test/smoke/.gitignore diff --git a/diktat-rules/src/test/resources/test/smoke/build.gradle_.kts b/diktat-ruleset/src/test/resources/test/smoke/build.gradle_.kts similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/build.gradle_.kts rename to diktat-ruleset/src/test/resources/test/smoke/build.gradle_.kts diff --git a/diktat-rules/src/test/resources/test/smoke/save.toml b/diktat-ruleset/src/test/resources/test/smoke/save.toml similarity index 96% rename from diktat-rules/src/test/resources/test/smoke/save.toml rename to diktat-ruleset/src/test/resources/test/smoke/save.toml index 54611e9109..bebb752e9d 100644 --- a/diktat-rules/src/test/resources/test/smoke/save.toml +++ b/diktat-ruleset/src/test/resources/test/smoke/save.toml @@ -5,6 +5,7 @@ description = "SmokeTest" suiteName = "SmokeTest" language = "Kotlin" expectedWarningsPattern = "// ;warn:?(.*):(\\d*): (.+)" +timeOutMillis = 3600000 ["fix and warn"] ["fix and warn".fix] diff --git a/diktat-rules/src/test/resources/test/smoke/src/jsMain/kotlin/org/cqfn/diktat/scripts/ScriptExpected.kt b/diktat-ruleset/src/test/resources/test/smoke/src/jsMain/kotlin/org/cqfn/diktat/scripts/ScriptExpected.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/jsMain/kotlin/org/cqfn/diktat/scripts/ScriptExpected.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/jsMain/kotlin/org/cqfn/diktat/scripts/ScriptExpected.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/jsMain/kotlin/org/cqfn/diktat/scripts/ScriptTest.kt b/diktat-ruleset/src/test/resources/test/smoke/src/jsMain/kotlin/org/cqfn/diktat/scripts/ScriptTest.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/jsMain/kotlin/org/cqfn/diktat/scripts/ScriptTest.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/jsMain/kotlin/org/cqfn/diktat/scripts/ScriptTest.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Bug1Expected.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Bug1Expected.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Bug1Expected.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Bug1Expected.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Bug1Test.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Bug1Test.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Bug1Test.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Bug1Test.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/DefaultPackageExpected.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/DefaultPackageExpected.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/DefaultPackageExpected.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/DefaultPackageExpected.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/DefaultPackageTest.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/DefaultPackageTest.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/DefaultPackageTest.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/DefaultPackageTest.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example1-2Expected.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example1-2Expected.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example1-2Expected.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example1-2Expected.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example1Expected.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example1Expected.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example1Expected.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example1Expected.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example1Test.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example1Test.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example1Test.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example1Test.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example2Expected.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example2Expected.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example2Expected.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example2Expected.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example2Test.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example2Test.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example2Test.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example2Test.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example3Expected.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example3Expected.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example3Expected.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example3Expected.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example3Test.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example3Test.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example3Test.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example3Test.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example4Expected.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example4Expected.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example4Expected.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example4Expected.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example4Test.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example4Test.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example4Test.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example4Test.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example5Expected.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example5Expected.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example5Expected.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example5Expected.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example5Test.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example5Test.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example5Test.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example5Test.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example6Expected.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example6Expected.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example6Expected.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example6Expected.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example6Test.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example6Test.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example6Test.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example6Test.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example7Expected.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example7Expected.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example7Expected.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example7Expected.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example7Test.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example7Test.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example7Test.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example7Test.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example8Expected.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example8Expected.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example8Expected.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example8Expected.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example8Test.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example8Test.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example8Test.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/Example8Test.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/KdocFormattingMultilineTagsExpected.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/KdocFormattingMultilineTagsExpected.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/KdocFormattingMultilineTagsExpected.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/KdocFormattingMultilineTagsExpected.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/KdocFormattingMultilineTagsTest.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/KdocFormattingMultilineTagsTest.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/KdocFormattingMultilineTagsTest.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/KdocFormattingMultilineTagsTest.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/LocalVariableWithOffsetExpected.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/LocalVariableWithOffsetExpected.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/LocalVariableWithOffsetExpected.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/LocalVariableWithOffsetExpected.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/LocalVariableWithOffsetTest.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/LocalVariableWithOffsetTest.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/LocalVariableWithOffsetTest.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/LocalVariableWithOffsetTest.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/ManyLineTransformInLongLineExpected.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/ManyLineTransformInLongLineExpected.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/ManyLineTransformInLongLineExpected.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/ManyLineTransformInLongLineExpected.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/ManyLineTransformInLongLineTest.kt b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/ManyLineTransformInLongLineTest.kt similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/ManyLineTransformInLongLineTest.kt rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/ManyLineTransformInLongLineTest.kt diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/kotlin-library-expected.gradle.kts b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/kotlin-library-expected.gradle.kts similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/kotlin-library-expected.gradle.kts rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/kotlin-library-expected.gradle.kts diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/kotlin-library.gradle.kts b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/kotlin-library.gradle.kts similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/kotlin-library.gradle.kts rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/kotlin-library.gradle.kts diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/save.toml b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/save.toml similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/save.toml rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/save.toml diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/script/PackageInScriptExpected.kts b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/script/PackageInScriptExpected.kts similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/script/PackageInScriptExpected.kts rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/script/PackageInScriptExpected.kts diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/script/PackageInScriptTest.kts b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/script/PackageInScriptTest.kts similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/script/PackageInScriptTest.kts rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/script/PackageInScriptTest.kts diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/script/SimpleRunInScriptExpected.kts b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/script/SimpleRunInScriptExpected.kts similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/script/SimpleRunInScriptExpected.kts rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/script/SimpleRunInScriptExpected.kts diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/script/SimpleRunInScriptTest.kts b/diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/script/SimpleRunInScriptTest.kts similarity index 100% rename from diktat-rules/src/test/resources/test/smoke/src/main/kotlin/script/SimpleRunInScriptTest.kts rename to diktat-ruleset/src/test/resources/test/smoke/src/main/kotlin/script/SimpleRunInScriptTest.kts diff --git a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/processing/TestComparatorUnit.kt b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/processing/TestComparatorUnit.kt index 36d592bcde..3d77f1419f 100644 --- a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/processing/TestComparatorUnit.kt +++ b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/processing/TestComparatorUnit.kt @@ -20,8 +20,10 @@ import kotlin.io.path.readLines * @property function a transformation that will be applied to the file */ @Suppress("ForbiddenComment", "TYPE_ALIAS") -class TestComparatorUnit(private val resourceFilePath: String, - private val function: (expectedText: String, testFilePath: String) -> String) { +class TestComparatorUnit( + private val resourceFilePath: String, + private val function: (expectedText: String, testFilePath: String) -> String, +) { /** * @param expectedResult the name of the resource which has the expected * content. The trailing newline, if any, **won't be read** as a separate @@ -32,7 +34,7 @@ class TestComparatorUnit(private val resourceFilePath: String, * @param testFileStr the name of the resource which has the original content. * @param trimLastEmptyLine whether the last (empty) line should be * discarded when reading the content of [testFileStr]. - * @return `true` if transformed file equals expected result, `false` otherwise. + * @return the result of file comparison by their content. * @see compareFilesFromFileSystem */ @Suppress("FUNCTION_BOOLEAN_PREFIX") @@ -40,18 +42,21 @@ class TestComparatorUnit(private val resourceFilePath: String, expectedResult: String, testFileStr: String, trimLastEmptyLine: Boolean = false - ): Boolean { + ): FileComparisonResult { val expectedPath = javaClass.classLoader.getResource("$resourceFilePath/$expectedResult") val testPath = javaClass.classLoader.getResource("$resourceFilePath/$testFileStr") if (testPath == null || expectedPath == null) { log.error("Not able to find files for running test: $expectedResult and $testFileStr") - return false + return FileComparisonResult( + isSuccessful = false, + actualContent = "// $resourceFilePath/$expectedResult is found: ${testPath != null}", + expectedContent = "// $resourceFilePath/$testFileStr is found: ${expectedPath != null}") } return compareFilesFromFileSystem( Paths.get(expectedPath.toURI()), Paths.get(testPath.toURI()), - trimLastEmptyLine).isSuccessful + trimLastEmptyLine) } /** diff --git a/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/util/TestUtils.kt b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/util/TestUtils.kt new file mode 100644 index 0000000000..564297cf38 --- /dev/null +++ b/diktat-test-framework/src/main/kotlin/org/cqfn/diktat/test/framework/util/TestUtils.kt @@ -0,0 +1,131 @@ +/** + * Utility classes and methods for tests + */ + +package org.cqfn.diktat.test.framework.util + +import org.cqfn.diktat.common.utils.loggerWithKtlintConfig + +import mu.KotlinLogging + +import java.io.IOException +import java.nio.file.FileVisitResult +import java.nio.file.FileVisitResult.CONTINUE +import java.nio.file.Files.walkFileTree +import java.nio.file.NoSuchFileException +import java.nio.file.Path +import java.nio.file.SimpleFileVisitor +import java.nio.file.attribute.BasicFileAttributes + +import kotlin.io.path.absolute +import kotlin.io.path.deleteExisting +import kotlin.io.path.deleteIfExists + +@Suppress("EMPTY_BLOCK_STRUCTURE_ERROR") +private val log = KotlinLogging.loggerWithKtlintConfig {} + +/** + * Deletes the file if it exists, retrying as necessary if the file is + * blocked by another process (on Windows). + * + * @receiver the file or empty directory. + * @see Path.deleteIfExists + */ +@Suppress( + "EMPTY_BLOCK_STRUCTURE_ERROR", + "MAGIC_NUMBER", +) +fun Path.deleteIfExistsSilently() { + val attempts = 10 + + val deleted = retry(attempts, delayMillis = 100L, lazyDefault = { false }) { + deleteIfExists() + + /* + * Ignore the return code of `deleteIfExists()` (will be `false` + * if the file doesn't exist). + */ + true + } + + if (!deleted) { + log.warn { + "File \"${absolute()}\" not deleted after $attempts attempt(s)." + } + } +} + +/** + * Deletes this directory recursively. + * + * @see Path.deleteIfExistsRecursively + */ +fun Path.deleteRecursively() { + walkFileTree(this, object : SimpleFileVisitor() { + override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult { + file.deleteIfExistsSilently() + return CONTINUE + } + + override fun postVisitDirectory(dir: Path, exc: IOException?): FileVisitResult { + dir.deleteExisting() + return CONTINUE + } + }) +} + +/** + * Deletes this directory recursively if it exists. + * + * @return `true` if the existing directory was successfully deleted, `false` if + * the directory doesn't exist. + * @see Files.deleteIfExists + * @see Path.deleteRecursively + */ +@Suppress("FUNCTION_BOOLEAN_PREFIX") +fun Path.deleteIfExistsRecursively(): Boolean = + try { + deleteRecursively() + true + } catch (_: NoSuchFileException) { + false + } + +/** + * Retries the execution of the [block]. + * + * @param attempts the number of attempts (must be positive). + * @param delayMillis the timeout (in milliseconds) between the consecutive + * attempts. The default is 0. Ignored if [attempts] is 1. + * @param lazyDefault allows to override the return value if none of the + * attempts succeeds. By default, the last exception is thrown. + * @param block the block to execute. + * @return the result of the execution of the [block], or whatever [lazyDefault] + * evaluates to if none of the attempts is successful. + */ +fun retry( + attempts: Int, + delayMillis: Long = 0L, + lazyDefault: (Throwable) -> T = { error -> throw error }, + block: () -> T +): T { + require(attempts > 0) { + "The number of attempts should be positive: $attempts" + } + + var lastError: Throwable? = null + + for (i in 1..attempts) { + try { + return block() + } catch (error: Throwable) { + lastError = error + } + + if (delayMillis > 0L) { + Thread.sleep(delayMillis) + } + } + + return lazyDefault(lastError ?: Exception("The block was never executed")) +} diff --git a/pom.xml b/pom.xml index cfff28661b..fe5327fbbb 100644 --- a/pom.xml +++ b/pom.xml @@ -49,6 +49,7 @@ 0.47.1 5.9.0 1.9.0 + 1.1.0 31.1-jre 1.7.36 1.5.0 @@ -60,7 +61,6 @@ 3.6.4 1.24 2.18.0 - 1.1.0 @@ -92,6 +92,11 @@ kotlin-stdlib ${kotlin.version} + + org.jetbrains.kotlin + kotlin-stdlib-jdk7 + ${kotlin.version} + org.jetbrains.kotlin kotlin-stdlib-jdk8 @@ -268,6 +273,35 @@ org.apache.maven.plugins maven-surefire-plugin 3.0.0-M7 + + + me.fabriciorby + maven-surefire-junit5-tree-reporter + ${surefire.junit5.tree-reporter.version} + + + + + + true + true + true + true + + + plain + + true + + + + true + true + + false + + org.apache.maven.plugins @@ -278,11 +312,35 @@ org.apache.maven.plugins maven-failsafe-plugin 3.0.0-M7 - - - org.apache.maven.plugins - maven-shade-plugin - 3.3.0 + + + me.fabriciorby + maven-surefire-junit5-tree-reporter + ${surefire.junit5.tree-reporter.version} + + + + + + true + true + true + true + + + plain + + true + + + + true + true + + false + + org.codehaus.mojo @@ -544,5 +602,14 @@ + + jdk11 + + [11, + + + 8 + +