diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index d593561daa..d008d171cb 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -23,7 +23,9 @@ jobs: restore-keys: | maven-build- - name: Maven Install - run: mvn -B clean install + run: mvn -B clean install -DskipTests + - name: Maven run test + run: mvn test - name: Code coverage report uses: codecov/codecov-action@v3 with: @@ -141,13 +143,25 @@ jobs: - name: Maven Install if: ${{ runner.os == 'Linux' || runner.os == 'macOS' }} run: | - mvn -B -T1C clean install + mvn -B -T1C clean install -DskipTests + shell: bash + + - name: Maven run test + if: ${{ runner.os == 'Linux' || runner.os == 'macOS' }} + run: | + mvn test shell: bash - name: Maven Install on windows if: runner.os == 'Windows' run: | - mvn -B -T1C clean install + mvn -B -T1C clean install -DskipTests + shell: cmd + + - name: Maven run test + if: runner.os == 'Windows' + run: | + mvn test shell: cmd # This step needs a Git repository, so it's impossible to extract it diff --git a/diktat-rules/pom.xml b/diktat-rules/pom.xml index 40d0bd03a5..5f5e28c1a5 100644 --- a/diktat-rules/pom.xml +++ b/diktat-rules/pom.xml @@ -15,6 +15,7 @@ 1.8 1.8 + 4.5.13 @@ -93,6 +94,11 @@ com.bpodgursky jbool_expressions + + org.apache.httpcomponents + httpclient + ${apache.httpclient.version} + diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt new file mode 100644 index 0000000000..1aa21c7036 --- /dev/null +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt @@ -0,0 +1,131 @@ +package org.cqfn.diktat.ruleset.smoke + +import org.cqfn.diktat.util.SAVE_VERSION +import org.apache.commons.io.FileUtils +import org.apache.http.client.methods.CloseableHttpResponse +import org.apache.http.client.methods.HttpGet +import org.apache.http.impl.client.HttpClients +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.condition.DisabledOnOs +import org.junit.jupiter.api.condition.OS +import java.io.File +import java.io.FileOutputStream +import java.nio.file.Paths +import kotlin.io.path.exists +import kotlin.io.path.listDirectoryEntries +import kotlin.io.path.name +import kotlin.io.path.pathString + +@DisabledOnOs(OS.MAC) +class DiktatSaveSmokeTest : DiktatSmokeTestBase() { + override val isLintErrors = false + override fun fixAndCompare( + config: String, + expected: String, + test: String, + ) { + saveSmokeTest(config, test) + } + + /** + * @param testPath path to file with code that will be transformed by formatter, relative to [resourceFilePath] + * @param configFilePath path of diktat-analysis file + */ + @Suppress("TOO_LONG_FUNCTION") + private fun saveSmokeTest( + configFilePath: String, + testPath: String + ) { + val processBuilder = createProcessBuilderWithCmd(testPath) + + val file = File("src/test/resources/test/smoke/tmpSave.txt") + val configFile = File("src/test/resources/test/smoke/diktat-analysis.yml") + val configFileFrom = File(configFilePath) + + configFile.createNewFile() + file.createNewFile() + + FileUtils.copyFile(configFileFrom, configFile) + + processBuilder.redirectErrorStream(true) + processBuilder.redirectOutput(ProcessBuilder.Redirect.appendTo(file)) + + val process = processBuilder.start() + process.waitFor() + + val output = file.readLines() + val saveOutput = output.joinToString("\n") + + file.delete() + + Assertions.assertTrue( + saveOutput.contains("SUCCESS") + ) + } + + companion object { + private const val KTLINT_VERSION = "0.46.1" + + 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 -> "" + } + + private fun downloadFile(url: String, file: File) { + val httpClient = HttpClients.createDefault() + val request = HttpGet(url) + httpClient.use { + val response: CloseableHttpResponse = httpClient.execute(request) + response.use { + val fileSave = response.entity + fileSave?.let { + FileOutputStream(file).use { outstream -> fileSave.writeTo(outstream) } + } + } + } + } + + @BeforeAll + @JvmStatic + internal fun beforeAll() { + val diktatDir: String = + Paths.get("../diktat-ruleset/target") + .takeIf { it.exists() } + ?.listDirectoryEntries() + ?.single { it.name.contains("diktat") } + ?.pathString ?: "" + + val diktat = File("src/test/resources/test/smoke/diktat.jar") + val diktatFrom = File(diktatDir) + val save = File("src/test/resources/test/smoke/${getSaveForCurrentOs()}") + val ktlint = File("src/test/resources/test/smoke/ktlint") + + ktlint.createNewFile() + save.createNewFile() + diktat.createNewFile() + + downloadFile("https://github.com/saveourtool/save-cli/releases/download/v$SAVE_VERSION/${getSaveForCurrentOs()}", save) + downloadFile("https://github.com/pinterest/ktlint/releases/download/$KTLINT_VERSION/ktlint", ktlint) + + FileUtils.copyFile(diktatFrom, diktat) + } + + @AfterAll + @JvmStatic + internal fun afterAll() { + val diktat = File("src/test/resources/test/smoke/diktat.jar") + val configFile = File("src/test/resources/test/smoke/diktat-analysis.yml") + val save = File("src/test/resources/test/smoke/${getSaveForCurrentOs()}") + val ktlint = File("src/test/resources/test/smoke/ktlint") + + diktat.delete() + configFile.delete() + ktlint.delete() + save.delete() + } + } +} 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 index 718c3785b5..d65b4ec4e9 100644 --- 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 @@ -2,93 +2,38 @@ 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.common.config.rules.RulesConfig -import org.cqfn.diktat.common.config.rules.RulesConfigReader -import org.cqfn.diktat.ruleset.constants.Warnings -import org.cqfn.diktat.ruleset.constants.Warnings.EMPTY_BLOCK_STRUCTURE_ERROR -import org.cqfn.diktat.ruleset.constants.Warnings.FILE_NAME_MATCH_CLASS -import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_NO_EMPTY_TAGS -import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_WITHOUT_PARAM_TAG 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 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.rules.chapter3.EmptyBlock -import org.cqfn.diktat.ruleset.rules.chapter6.classes.InlineClassesRule -import org.cqfn.diktat.util.FixTestBase import org.cqfn.diktat.util.assertEquals -import com.charleskorn.kaml.Yaml -import com.charleskorn.kaml.YamlConfiguration import com.pinterest.ktlint.core.LintError -import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Tag import org.junit.jupiter.api.Test import java.io.File -import java.time.LocalDate - -import kotlin.io.path.createTempFile -import kotlinx.serialization.builtins.ListSerializer - -typealias RuleToConfig = Map> /** * 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 : FixTestBase("test/smoke/src/main/kotlin", - { DiktatRuleSetProvider(configFilePath) }, - { lintError, _ -> unfixedLintErrors.add(lintError) }, - null -) { - /** - * Disable some of the rules. - */ - @Suppress("UnsafeCallOnNullableType") - private fun overrideRulesConfig(rulesToDisable: List, rulesToOverride: RuleToConfig = emptyMap()) { - val rulesConfig = RulesConfigReader(javaClass.classLoader).readResource(configFilePath)!! - .toMutableList() - .also { rulesConfig -> - rulesToDisable.forEach { warning -> - rulesConfig.removeIf { it.name == warning.name } - rulesConfig.add(RulesConfig(warning.name, enabled = false, configuration = emptyMap())) - } - rulesToOverride.forEach { (warning, configuration) -> - rulesConfig.removeIf { it.name == warning } - rulesConfig.add(RulesConfig(warning, enabled = true, configuration = configuration)) - } - } - .toList() - createTempFile().toFile() - .also { - configFilePath = it.absolutePath - } - .writeText( - Yaml(configuration = YamlConfiguration(strictMode = true)) - .encodeToString(ListSerializer(RulesConfig.serializer()), rulesConfig) - ) - } - - @BeforeEach - fun setUp() { - unfixedLintErrors.clear() - } - - @AfterEach - fun tearDown() { - configFilePath = DEFAULT_CONFIG_PATH +class DiktatSmokeTest : DiktatSmokeTestBase() { + override val isLintErrors = true + override fun fixAndCompare( + config: String, + expected: String, + test: String, + ) { + fixAndCompareSmokeTest(expected, test) } @Test @@ -98,141 +43,6 @@ class DiktatSmokeTest : FixTestBase("test/smoke/src/main/kotlin", "../../jsMain/kotlin/org/cqfn/diktat/scripts/ScriptTest.kt") } - @Test - @Tag("DiktatRuleSetProvider") - fun `regression - should not fail if package is not set`() { - overrideRulesConfig(listOf(Warnings.PACKAGE_NAME_MISSING, Warnings.PACKAGE_NAME_INCORRECT_PATH, - Warnings.PACKAGE_NAME_INCORRECT_PREFIX)) - fixAndCompareSmokeTest("DefaultPackageExpected.kt", "DefaultPackageTest.kt") - } - - @Test - @Tag("DiktatRuleSetProvider") - fun `smoke test #8 - anonymous function`() { - fixAndCompareSmokeTest("Example8Expected.kt", "Example8Test.kt") - } - - @Test - @Tag("DiktatRuleSetProvider") - fun `smoke test #7`() { - fixAndCompareSmokeTest("Example7Expected.kt", "Example7Test.kt") - } - - @Test - @Tag("DiktatRuleSetProvider") - fun `smoke test #6`() { - overrideRulesConfig( - rulesToDisable = emptyList(), - rulesToOverride = mapOf( - WRONG_INDENTATION.name to mapOf( - "extendedIndentForExpressionBodies" to "true", - "extendedIndentBeforeDot" to "true", - ) - ) - ) - fixAndCompareSmokeTest("Example6Expected.kt", "Example6Test.kt") - } - - @Test - @Tag("DiktatRuleSetProvider") - fun `smoke test #5`() { - overrideRulesConfig(emptyList(), - mapOf( - Warnings.HEADER_MISSING_OR_WRONG_COPYRIGHT.name to mapOf( - "isCopyrightMandatory" to "true", - "copyrightText" to """|Copyright 2018-${LocalDate.now().year} John Doe. - | Licensed under the Apache License, Version 2.0 (the "License"); - | you may not use this file except in compliance with the License. - | You may obtain a copy of the License at - | - | http://www.apache.org/licenses/LICENSE-2.0 - """.trimMargin() - ), - DIKTAT_COMMON to mapOf( - "domainName" to "org.cqfn.diktat", - "kotlinVersion" to "1.3.7" - ) - ) - ) - fixAndCompareSmokeTest("Example5Expected.kt", "Example5Test.kt") - - Assertions.assertFalse( - unfixedLintErrors.contains(LintError(line = 1, col = 1, ruleId = "diktat-ruleset:${CommentsRule.NAME_ID}", detail = "${Warnings.COMMENTED_OUT_CODE.warnText()} /*")) - ) - - Assertions.assertTrue( - unfixedLintErrors.contains(LintError(1, 1, "diktat-ruleset:${InlineClassesRule.NAME_ID}", "${Warnings.INLINE_CLASS_CAN_BE_USED.warnText()} class Some")) - ) - } - - @Test - @Tag("DiktatRuleSetProvider") - fun `smoke test #4`() { - fixAndCompareSmokeTest("Example4Expected.kt", "Example4Test.kt") - } - - @Test - @Tag("DiktatRuleSetProvider") - fun `smoke test #3`() { - fixAndCompareSmokeTest("Example3Expected.kt", "Example3Test.kt") - } - - @Test - @Tag("DiktatRuleSetProvider") - fun `regression - shouldn't throw exception in cases similar to #371`() { - fixAndCompareSmokeTest("Bug1Expected.kt", "Bug1Test.kt") - } - - @Test - @Tag("DiktatRuleSetProvider") - fun `smoke test #2`() { - overrideRulesConfig( - rulesToDisable = emptyList(), - rulesToOverride = mapOf( - WRONG_INDENTATION.name to mapOf( - "extendedIndentBeforeDot" to "true", - ) - ) - ) - fixAndCompareSmokeTest("Example2Expected.kt", "Example2Test.kt") - unfixedLintErrors.assertEquals( - 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), - LintError(15, 23, "$DIKTAT_RULE_SET_ID:${KdocMethods.NAME_ID}", - "${KDOC_WITHOUT_PARAM_TAG.warnText()} createWithFile (containerName)", true), - LintError(31, 14, "$DIKTAT_RULE_SET_ID:${EmptyBlock.NAME_ID}", - "${EMPTY_BLOCK_STRUCTURE_ERROR.warnText()} empty blocks are forbidden unless it is function with override keyword", false) - ) - } - - @Test - @Tag("DiktatRuleSetProvider") - fun `smoke test #1`() { - overrideRulesConfig( - rulesToDisable = emptyList(), - rulesToOverride = mapOf( - WRONG_INDENTATION.name to mapOf( - "extendedIndentForExpressionBodies" to "true", - ) - ) - ) - fixAndCompareSmokeTest("Example1Expected.kt", "Example1Test.kt") - unfixedLintErrors.assertEquals( - 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), - LintError(3, 6, "$DIKTAT_RULE_SET_ID:${KdocComments.NAME_ID}", "${MISSING_KDOC_TOP_LEVEL.warnText()} Example", false), - LintError(3, 26, "$DIKTAT_RULE_SET_ID:${KdocComments.NAME_ID}", "${MISSING_KDOC_CLASS_ELEMENTS.warnText()} isValid", false), - LintError(6, 9, "$DIKTAT_RULE_SET_ID:${KdocComments.NAME_ID}", "${MISSING_KDOC_CLASS_ELEMENTS.warnText()} foo", false), - LintError(8, 8, "$DIKTAT_RULE_SET_ID:${KdocComments.NAME_ID}", "${MISSING_KDOC_CLASS_ELEMENTS.warnText()} foo", false), - LintError(8, 8, "$DIKTAT_RULE_SET_ID:${KdocMethods.NAME_ID}", "${MISSING_KDOC_ON_FUNCTION.warnText()} foo", false), - LintError(9, 3, "$DIKTAT_RULE_SET_ID:${EmptyBlock.NAME_ID}", EMPTY_BLOCK_STRUCTURE_ERROR.warnText() + - " empty blocks are forbidden unless it is function with override keyword", false), - LintError(12, 10, "$DIKTAT_RULE_SET_ID:${KdocFormatting.NAME_ID}", "${KDOC_NO_EMPTY_TAGS.warnText()} @return", false), - LintError(14, 8, "$DIKTAT_RULE_SET_ID:${KdocFormatting.NAME_ID}", "${KDOC_NO_EMPTY_TAGS.warnText()} @return", false), - LintError(19, 20, "$DIKTAT_RULE_SET_ID:${KdocFormatting.NAME_ID}", "${KDOC_NO_EMPTY_TAGS.warnText()} @return", false) - ) - } - @Test @Tag("DiktatRuleSetProvider") fun `smoke test with kts files`() { @@ -270,20 +80,6 @@ class DiktatSmokeTest : FixTestBase("test/smoke/src/main/kotlin", ) } - @Test - @Tag("DiktatRuleSetProvider") - fun `smoke test with kts files #2`() { - fixAndCompareSmokeTest("script/SimpleRunInScriptExpected.kts", "script/SimpleRunInScriptTest.kts") - Assertions.assertEquals(7, unfixedLintErrors.size) - } - - @Test - @Tag("DiktatRuleSetProvider") - fun `smoke test with kts files with package name`() { - fixAndCompareSmokeTest("script/PackageInScriptExpected.kts", "script/PackageInScriptTest.kts") - Assertions.assertEquals(7, unfixedLintErrors.size) - } - @Test @Tag("DiktatRuleSetProvider") fun `disable charters`() { @@ -310,45 +106,14 @@ class DiktatSmokeTest : FixTestBase("test/smoke/src/main/kotlin", ) } - @Test - @Tag("DiktatRuleSetProvider") - fun `regression - should correctly handle tags with empty lines`() { - fixAndCompareSmokeTest("KdocFormattingMultilineTagsExpected.kt", "KdocFormattingMultilineTagsTest.kt") - } - - @Test - @Tag("DiktatRuleSetProvider") - fun `regression - FP of local variables rule`() { - fixAndCompareSmokeTest("LocalVariableWithOffsetExpected.kt", "LocalVariableWithOffsetTest.kt") - org.assertj - .core - .api - .Assertions - .assertThat(unfixedLintErrors) - .noneMatch { - it.ruleId == "diktat-ruleset:local-variables" - } - } - - @Test - @Tag("DiktatRuleSetProvider") - fun `fix can cause long line`() { - overrideRulesConfig( - rulesToDisable = emptyList(), - rulesToOverride = mapOf( - WRONG_INDENTATION.name to mapOf( - "extendedIndentAfterOperators" to "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) ) - fixAndCompareSmokeTest("ManyLineTransformInLongLineExpected.kt", "ManyLineTransformInLongLineTest.kt") - } - - companion object { - private const val DEFAULT_CONFIG_PATH = "../diktat-analysis.yml" - private val unfixedLintErrors: MutableList = mutableListOf() - - // by default using same yml config as for diktat code style check, but allow to override - private var configFilePath = DEFAULT_CONFIG_PATH } } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTestBase.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTestBase.kt new file mode 100644 index 0000000000..5c54544ca1 --- /dev/null +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/smoke/DiktatSmokeTestBase.kt @@ -0,0 +1,299 @@ +@file:Suppress( + "MISSING_KDOC_CLASS_ELEMENTS", + "MISSING_KDOC_ON_FUNCTION", + "BACKTICKS_PROHIBITED", +) + +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.common.config.rules.RulesConfig +import org.cqfn.diktat.common.config.rules.RulesConfigReader +import org.cqfn.diktat.ruleset.constants.Warnings +import org.cqfn.diktat.ruleset.constants.Warnings.EMPTY_BLOCK_STRUCTURE_ERROR +import org.cqfn.diktat.ruleset.constants.Warnings.FILE_NAME_MATCH_CLASS +import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE +import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_NO_EMPTY_TAGS +import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_WITHOUT_PARAM_TAG +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 +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.rules.chapter3.EmptyBlock +import org.cqfn.diktat.ruleset.rules.chapter6.classes.InlineClassesRule +import org.cqfn.diktat.util.FixTestBase +import org.cqfn.diktat.util.assertEquals + +import com.charleskorn.kaml.Yaml +import com.charleskorn.kaml.YamlConfiguration +import com.pinterest.ktlint.core.LintError +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Tag +import org.junit.jupiter.api.Test + +import java.time.LocalDate + +import kotlinx.serialization.builtins.ListSerializer + +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 + + /** + * Disable some of the rules. + * + * @param rulesToDisable + * @param rulesToOverride + */ + @Suppress("UnsafeCallOnNullableType") + open fun overrideRulesConfig(rulesToDisable: List, rulesToOverride: RuleToConfig = emptyMap()) { + val rulesConfig = RulesConfigReader(javaClass.classLoader).readResource(DEFAULT_CONFIG_PATH)!! + .toMutableList() + .also { rulesConfig -> + rulesToDisable.forEach { warning -> + rulesConfig.removeIf { it.name == warning.name } + rulesConfig.add(RulesConfig(warning.name, enabled = false, configuration = emptyMap())) + } + rulesToOverride.forEach { (warning, configuration) -> + rulesConfig.removeIf { it.name == warning } + rulesConfig.add(RulesConfig(warning, enabled = true, configuration = configuration)) + } + } + .toList() + kotlin.io.path.createTempFile() + .toFile() + .also { + configFilePath = it.absolutePath + } + .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") + fun `regression - should not fail if package is not set`() { + overrideRulesConfig(listOf(Warnings.PACKAGE_NAME_MISSING, Warnings.PACKAGE_NAME_INCORRECT_PATH, + Warnings.PACKAGE_NAME_INCORRECT_PREFIX)) + fixAndCompare(configFilePath, "DefaultPackageExpected.kt", "DefaultPackageTest.kt") + } + + @Test + @Tag("DiktatRuleSetProvider") + fun `smoke test #8 - anonymous function`() { + fixAndCompare(configFilePath, "Example8Expected.kt", "Example8Test.kt") + } + + @Test + @Tag("DiktatRuleSetProvider") + fun `smoke test #7`() { + fixAndCompare(configFilePath, "Example7Expected.kt", "Example7Test.kt") + } + + @Test + @Tag("DiktatRuleSetProvider") + fun `smoke test #6`() { + overrideRulesConfig( + rulesToDisable = emptyList(), + rulesToOverride = mapOf( + WRONG_INDENTATION.name to mapOf( + "extendedIndentForExpressionBodies" to "true", + "extendedIndentAfterOperators" to "true", + "extendedIndentBeforeDot" to "true", + ) + ) + ) + fixAndCompare(configFilePath, "Example6Expected.kt", "Example6Test.kt") + } + + @Test + @Tag("DiktatRuleSetProvider") + fun `smoke test #5`() { + overrideRulesConfig(emptyList(), + mapOf( + Warnings.HEADER_MISSING_OR_WRONG_COPYRIGHT.name to mapOf( + "isCopyrightMandatory" to "true", + "copyrightText" to """|Copyright 2018-${LocalDate.now().year} John Doe. + | Licensed under the Apache License, Version 2.0 (the "License"); + | you may not use this file except in compliance with the License. + | You may obtain a copy of the License at + | + | http://www.apache.org/licenses/LICENSE-2.0 + """.trimMargin() + ), + DIKTAT_COMMON to mapOf( + "domainName" to "org.cqfn.diktat", + "kotlinVersion" to "1.3.7" + ) + ) + ) + fixAndCompare(configFilePath, "Example5Expected.kt", "Example5Test.kt") + + if (isLintErrors) { + Assertions.assertFalse( + unfixedLintErrors.contains(LintError(line = 1, col = 1, ruleId = "diktat-ruleset:${CommentsRule.NAME_ID}", detail = "${Warnings.COMMENTED_OUT_CODE.warnText()} /*")) + ) + + Assertions.assertTrue( + unfixedLintErrors.contains(LintError(1, 1, "diktat-ruleset:${InlineClassesRule.NAME_ID}", "${Warnings.INLINE_CLASS_CAN_BE_USED.warnText()} class Some")) + ) + } + } + + @Test + @Tag("DiktatRuleSetProvider") + fun `smoke test #4`() { + fixAndCompare(configFilePath, "Example4Expected.kt", "Example4Test.kt") + } + + @Test + @Tag("DiktatRuleSetProvider") + fun `smoke test #3`() { + fixAndCompare(configFilePath, "Example3Expected.kt", "Example3Test.kt") + } + + @Test + @Tag("DiktatRuleSetProvider") + fun `regression - shouldn't throw exception in cases similar to #371`() { + fixAndCompare(configFilePath, "Bug1Expected.kt", "Bug1Test.kt") + } + + @Test + @Tag("DiktatRuleSetProvider") + fun `smoke test #2`() { + overrideRulesConfig( + rulesToDisable = emptyList(), + rulesToOverride = mapOf( + WRONG_INDENTATION.name to mapOf( + "extendedIndentAfterOperators" to "true", + "extendedIndentBeforeDot" to "true", + ) + ) + ) + fixAndCompare(configFilePath, "Example2Expected.kt", "Example2Test.kt") + if (isLintErrors) { + unfixedLintErrors.assertEquals( + 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), + LintError(15, 23, "$DIKTAT_RULE_SET_ID:${KdocMethods.NAME_ID}", + "${KDOC_WITHOUT_PARAM_TAG.warnText()} createWithFile (containerName)", true), + LintError(31, 14, "$DIKTAT_RULE_SET_ID:${EmptyBlock.NAME_ID}", + "${EMPTY_BLOCK_STRUCTURE_ERROR.warnText()} empty blocks are forbidden unless it is function with override keyword", false) + ) + } + } + + @Test + @Tag("DiktatRuleSetProvider") + fun `smoke test #1`() { + overrideRulesConfig( + rulesToDisable = emptyList(), + rulesToOverride = mapOf( + WRONG_INDENTATION.name to mapOf( + "extendedIndentAfterOperators" to "true", + "extendedIndentForExpressionBodies" to "true", + ) + ) + ) + fixAndCompare(configFilePath, "Example1Expected.kt", "Example1Test.kt") + if (isLintErrors) { + unfixedLintErrors.assertEquals( + 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), + LintError(3, 6, "$DIKTAT_RULE_SET_ID:${KdocComments.NAME_ID}", "${MISSING_KDOC_TOP_LEVEL.warnText()} Example", false), + LintError(3, 26, "$DIKTAT_RULE_SET_ID:${KdocComments.NAME_ID}", "${MISSING_KDOC_CLASS_ELEMENTS.warnText()} isValid", false), + LintError(6, 9, "$DIKTAT_RULE_SET_ID:${KdocComments.NAME_ID}", "${MISSING_KDOC_CLASS_ELEMENTS.warnText()} foo", false), + LintError(8, 8, "$DIKTAT_RULE_SET_ID:${KdocComments.NAME_ID}", "${MISSING_KDOC_CLASS_ELEMENTS.warnText()} foo", false), + LintError(8, 8, "$DIKTAT_RULE_SET_ID:${KdocMethods.NAME_ID}", "${MISSING_KDOC_ON_FUNCTION.warnText()} foo", false), + LintError(9, 3, "$DIKTAT_RULE_SET_ID:${EmptyBlock.NAME_ID}", EMPTY_BLOCK_STRUCTURE_ERROR.warnText() + + " empty blocks are forbidden unless it is function with override keyword", false), + LintError(12, 10, "$DIKTAT_RULE_SET_ID:${KdocFormatting.NAME_ID}", "${KDOC_NO_EMPTY_TAGS.warnText()} @return", false), + LintError(14, 8, "$DIKTAT_RULE_SET_ID:${KdocFormatting.NAME_ID}", "${KDOC_NO_EMPTY_TAGS.warnText()} @return", false), + LintError(19, 20, "$DIKTAT_RULE_SET_ID:${KdocFormatting.NAME_ID}", "${KDOC_NO_EMPTY_TAGS.warnText()} @return", false) + ) + } + } + + @Test + @Tag("DiktatRuleSetProvider") + fun `smoke test with kts files #2`() { + fixAndCompare(configFilePath, "script/SimpleRunInScriptExpected.kts", "script/SimpleRunInScriptTest.kts") + } + + @Test + @Tag("DiktatRuleSetProvider") + fun `smoke test with kts files with package name`() { + fixAndCompare(configFilePath, "script/PackageInScriptExpected.kts", "script/PackageInScriptTest.kts") + } + + @Test + @Tag("DiktatRuleSetProvider") + fun `regression - should correctly handle tags with empty lines`() { + fixAndCompare(configFilePath, "KdocFormattingMultilineTagsExpected.kt", "KdocFormattingMultilineTagsTest.kt") + } + + @Test + @Tag("DiktatRuleSetProvider") + fun `regression - FP of local variables rule`() { + fixAndCompare(configFilePath, "LocalVariableWithOffsetExpected.kt", "LocalVariableWithOffsetTest.kt") + } + + @Test + @Tag("DiktatRuleSetProvider") + fun `fix can cause long line`() { + overrideRulesConfig( + rulesToDisable = emptyList(), + rulesToOverride = mapOf( + WRONG_INDENTATION.name to mapOf( + "extendedIndentAfterOperators" to "false", + ) + ) + ) + fixAndCompare(configFilePath, "ManyLineTransformInLongLineExpected.kt", "ManyLineTransformInLongLineTest.kt") + } + + abstract fun fixAndCompare( + config: String, + expected: String, + test: String, + ) + + companion object { + const val DEFAULT_CONFIG_PATH = "../diktat-analysis.yml" + val unfixedLintErrors: MutableList = mutableListOf() + + // by default using same yml config as for diktat code style check, but allow to override + var configFilePath = DEFAULT_CONFIG_PATH + } +} 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 8d53a40e53..59d1a4ce78 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 @@ -3,25 +3,29 @@ package org.cqfn.diktat.util import org.cqfn.diktat.common.config.rules.RulesConfig 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 */ -open class FixTestBase(protected val resourceFilePath: String, - private val ruleSetProviderRef: (rulesConfigList: List?) -> RuleSetProvider, - private val cb: LintErrorCallback = defaultCallback, - private val rulesConfigList: List? = null +open class FixTestBase( + protected val resourceFilePath: String, + private val ruleSetProviderRef: (rulesConfigList: List?) -> RuleSetProvider, + private val cb: LintErrorCallback = defaultCallback, + private val rulesConfigList: List? = null, ) { - private val testComparatorUnit = TestComparatorUnit(resourceFilePath) { text, fileName -> + /** + * testComparatorUnit + */ + protected val testComparatorUnit = TestComparatorUnit(resourceFilePath) { text, fileName -> format(ruleSetProviderRef, text, fileName, rulesConfigList, cb = cb) } @@ -47,15 +51,28 @@ open class FixTestBase(protected val resourceFilePath: String, ) } + 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 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] + * @return ProcessBuilder */ - protected fun fixAndCompareSmokeTest(expectedPath: String, testPath: String) { - Assertions.assertTrue( - testComparatorUnit - .compareFilesFromResources(expectedPath, testPath, true) - ) + 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 src/test/resources/test/smoke/src/main/kotlin $testPath --log all") + else -> ProcessBuilder(savePath, "src/test/resources/test/smoke/src/main/kotlin", testPath) + } + return result } /** diff --git a/diktat-rules/src/test/resources/test/smoke/save.toml b/diktat-rules/src/test/resources/test/smoke/save.toml index 6c886524d3..18bedb7070 100644 --- a/diktat-rules/src/test/resources/test/smoke/save.toml +++ b/diktat-rules/src/test/resources/test/smoke/save.toml @@ -1,8 +1,13 @@ [general] -execCmd="java -jar ktlint-snap.jar -R diktat-1.0.0-rc.2.jar" +execCmd="java -jar ktlint -R diktat.jar" tags = ["smokeTest"] description = "SmokeTest" suiteName = "SmokeTest" +language = "Kotlin" +expectedWarningsPattern = "// ;warn:?(.*):(\\d*): (.+)" -[fix] - execFlags="--disabled_rules 'standard' -F" +["fix and warn"] + ["fix and warn".fix] + execFlags="--disabled_rules=standard -F" + ["fix and warn".warn] + actualWarningsPattern = "(\\w+\\..+):(\\d+):(\\d+): (\\[.*\\].*)$" diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Bug1Expected.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Bug1Expected.kt index 65336d9bd1..368c0857db 100644 --- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Bug1Expected.kt +++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Bug1Expected.kt @@ -1,19 +1,19 @@ -// ;warn:1:1: [FILE_NAME_MATCH_CLASS] file name is incorrect - it should match with the class described in it if there is the only one class declared: Bug1Expected.kt vs D +// ;warn:1:1: [FILE_NAME_MATCH_CLASS] file name is incorrect - it should match with the class described in it if there is the only one class declared: Bug1Expected.kt vs D{{.*}} package org.cqfn.diktat -// ;warn:4:1: [MISSING_KDOC_TOP_LEVEL] all public and internal top-level classes and functions should have Kdoc: D (cannot be auto-corrected) -// ;warn:7:7: [CLASS_NAME_INCORRECT] class/enum/interface name should be in PascalCase and should contain only latin (ASCII) letters or numbers: D -// ;warn:7:7: [IDENTIFIER_LENGTH] identifier's length is incorrect, it should be in range of [2, 64] symbols: D (cannot be auto-corrected) +// ;warn:4:1: [MISSING_KDOC_TOP_LEVEL] all public and internal top-level classes and functions should have Kdoc: D (cannot be auto-corrected){{.*}} +// ;warn:7:7: [CLASS_NAME_INCORRECT] class/enum/interface name should be in PascalCase and should contain only latin (ASCII) letters or numbers: D{{.*}} +// ;warn:7:7: [IDENTIFIER_LENGTH] identifier's length is incorrect, it should be in range of [2, 64] symbols: D (cannot be auto-corrected){{.*}} class D { - // ;warn:8:5: [MISSING_KDOC_CLASS_ELEMENTS] all public, internal and protected classes, functions and variables inside the class should have Kdoc: x (cannot be auto-corrected) + // ;warn:8:5: [MISSING_KDOC_CLASS_ELEMENTS] all public, internal and protected classes, functions and variables inside the class should have Kdoc: x (cannot be auto-corrected){{.*}} val x = 0 - // ;warn:13:8: [KDOC_NO_EMPTY_TAGS] no empty descriptions in tag blocks are allowed: @return (cannot be auto-corrected) + // ;warn:13:8: [KDOC_NO_EMPTY_TAGS] no empty descriptions in tag blocks are allowed: @return (cannot be auto-corrected){{.*}} /** * @return */ fun bar(): Bar { - // ;warn:17:19: [MAGIC_NUMBER] avoid using magic numbers, instead define constants with clear names describing what the magic number means: 42 (cannot be auto-corrected) + // ;warn:17:19: [MAGIC_NUMBER] avoid using magic numbers, instead define constants with clear names describing what the magic number means: 42 (cannot be auto-corrected){{.*}} val qux = 42 return Bar(qux) } diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/DefaultPackageExpected.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/DefaultPackageExpected.kt index 617642d232..f3b88fc061 100644 --- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/DefaultPackageExpected.kt +++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/DefaultPackageExpected.kt @@ -1,3 +1,5 @@ +// ;warn:1:1: [FILE_NAME_MATCH_CLASS] file name is incorrect - it should match with the class described in it if there is the only one class declared: DefaultPackageExpected.kt vs Example{{.*}} +// ;warn:3:1: [FILE_INCORRECT_BLOCKS_ORDER] general structure of kotlin source file is wrong, parts are in incorrect order: @file:Suppress{{.*}} @file:Suppress( "PACKAGE_NAME_MISSING", "PACKAGE_NAME_INCORRECT_PATH", diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example1Expected.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example1Expected.kt index fa3e6f85d0..e07f59aa22 100644 --- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example1Expected.kt +++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example1Expected.kt @@ -1,10 +1,21 @@ +// ;warn:1:1: [FILE_NAME_MATCH_CLASS] file name is incorrect - it should match with the class described in it if there is the only one class declared: Example1Expected.kt vs Example{{.*}} package org.cqfn.diktat +// ;warn:4:1: [MISSING_KDOC_TOP_LEVEL] all public and internal top-level classes and functions should have Kdoc: Example (cannot be auto-corrected){{.*}} class Example { + // ;warn:6:5: [MISSING_KDOC_CLASS_ELEMENTS] all public, internal and protected classes, functions and variables inside the class should have Kdoc: isValid (cannot be auto-corrected){{.*}} + // ;warn:10:5: [MISSING_KDOC_CLASS_ELEMENTS] all public, internal and protected classes, functions and variables inside the class should have Kdoc: foo (cannot be auto-corrected){{.*}} @get:JvmName("getIsValid") val isValid = true val foo: Int = 1 + // ;warn:12:5: [MISSING_KDOC_CLASS_ELEMENTS] all public, internal and protected classes, functions and variables inside the class should have Kdoc: foo (cannot be auto-corrected){{.*}} + // ;warn:12:5: [MISSING_KDOC_ON_FUNCTION] all public, internal and protected functions should have Kdoc with proper tags: foo (cannot be auto-corrected){{.*}} + // ;warn:19:19: [EMPTY_BLOCK_STRUCTURE_ERROR] incorrect format of empty block: empty blocks are forbidden unless it is function with override keyword (cannot be auto-corrected){{.*}} + // ;warn:24:8: [KDOC_NO_EMPTY_TAGS] no empty descriptions in tag blocks are allowed: @return (cannot be auto-corrected){{.*}} + // ;warn:30:8: [KDOC_NO_EMPTY_TAGS] no empty descriptions in tag blocks are allowed: @return (cannot be auto-corrected){{.*}} + // ;warn:38:8: [KDOC_NO_EMPTY_TAGS] no empty descriptions in tag blocks are allowed: @return (cannot be auto-corrected){{.*}} + // ;warn:47:8: [KDOC_NO_EMPTY_TAGS] no empty descriptions in tag blocks are allowed: @return (cannot be auto-corrected){{.*}} fun Foo.foo() { } /** diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example2Expected.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example2Expected.kt index 8b068a2bac..4cda77c1dd 100644 --- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example2Expected.kt +++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example2Expected.kt @@ -1,3 +1,4 @@ +// ;warn:1:1: [HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE] files that contain multiple or no classes should contain description of what is inside of this file: there are 2 declared classes and/or objects (cannot be auto-corrected){{.*}} package org.cqfn.diktat import org.slf4j.LoggerFactory @@ -11,6 +12,7 @@ import java.util.Properties */ @ExperimentalStdlibApi public data class Example(val foo: Int, val bar: Double) : SuperExample("lorem ipsum") +// ;warn:36:3: [EMPTY_BLOCK_STRUCTURE_ERROR] incorrect format of empty block: empty blocks are forbidden unless it is function with override keyword (cannot be auto-corrected){{.*}} private class TestException : Exception() /* this class is unused */ // private class Test : RuntimeException() diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example3Expected.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example3Expected.kt index 46f4f61784..e464fd1eda 100644 --- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example3Expected.kt +++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example3Expected.kt @@ -1,3 +1,4 @@ +// ;warn:1:1: [HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE] files that contain multiple or no classes should contain description of what is inside of this file: there are 6 declared classes and/or objects (cannot be auto-corrected){{.*}} package org.cqfn.diktat /* @@ -8,14 +9,23 @@ package org.cqfn.diktat * @property name */ class HttpClient(var name: String) { + // ;warn:12:5: [MISSING_KDOC_CLASS_ELEMENTS] all public, internal and protected classes, functions and variables inside the class should have Kdoc: url (cannot be auto-corrected){{.*}} + // ;warn:16:5: [MISSING_KDOC_CLASS_ELEMENTS] all public, internal and protected classes, functions and variables inside the class should have Kdoc: port (cannot be auto-corrected){{.*}} + // ;warn:17:5: [MISSING_KDOC_CLASS_ELEMENTS] all public, internal and protected classes, functions and variables inside the class should have Kdoc: timeout (cannot be auto-corrected){{.*}} var url: String = "" var port: String = "" var timeout = 0 + // ;warn:19:5: [MISSING_KDOC_CLASS_ELEMENTS] all public, internal and protected classes, functions and variables inside the class should have Kdoc: doRequest (cannot be auto-corrected){{.*}} + // ;warn:19:5: [MISSING_KDOC_ON_FUNCTION] all public, internal and protected functions should have Kdoc with proper tags: doRequest (cannot be auto-corrected){{.*}} + // ;warn:22:21: [EMPTY_BLOCK_STRUCTURE_ERROR] incorrect format of empty block: empty blocks are forbidden unless it is function with override keyword (cannot be auto-corrected){{.*}} fun doRequest() {} } +// ;warn:25:1: [MISSING_KDOC_TOP_LEVEL] all public and internal top-level classes and functions should have Kdoc: Example (cannot be auto-corrected){{.*}} class Example { + // ;warn:27:5: [MISSING_KDOC_CLASS_ELEMENTS] all public, internal and protected classes, functions and variables inside the class should have Kdoc: foo (cannot be auto-corrected){{.*}} + // ;warn:27:5: [MISSING_KDOC_ON_FUNCTION] all public, internal and protected functions should have Kdoc with proper tags: foo (cannot be auto-corrected){{.*}} fun foo() { if (condition1 && condition2) { bar() @@ -33,10 +43,13 @@ class Example { } } +// ;warn:46:1: [MISSING_KDOC_TOP_LEVEL] all public and internal top-level classes and functions should have Kdoc: IssueType (cannot be auto-corrected){{.*}} enum class IssueType { PROJECT_STRUCTURE, TESTS, VCS } +// ;warn:51:1: [MISSING_KDOC_TOP_LEVEL] all public and internal top-level classes and functions should have Kdoc: IssueType2 (cannot be auto-corrected){{.*}} +// ;warn:61:8: [KDOC_NO_EMPTY_TAGS] no empty descriptions in tag blocks are allowed: @return (cannot be auto-corrected){{.*}} enum class IssueType2 { PROJECT_STRUCTURE, TESTS, @@ -52,6 +65,13 @@ enum class IssueType2 { companion object } +// ;warn:68:1: [MISSING_KDOC_TOP_LEVEL] all public and internal top-level classes and functions should have Kdoc: IssueType3 (cannot be auto-corrected){{.*}} +// ;warn:76:5: [IDENTIFIER_LENGTH] identifier's length is incorrect, it should be in range of [2, 64] symbols: A (cannot be auto-corrected){{.*}} +// ;warn:77:5: [CONFUSING_IDENTIFIER_NAMING] it's a bad name for identifier: better name is: bt, nxt (cannot be auto-corrected){{.*}} +// ;warn:77:5: [IDENTIFIER_LENGTH] identifier's length is incorrect, it should be in range of [2, 64] symbols: B (cannot be auto-corrected){{.*}} +// ;warn:78:5: [IDENTIFIER_LENGTH] identifier's length is incorrect, it should be in range of [2, 64] symbols: C (cannot be auto-corrected){{.*}} +// ;warn:79:5: [CONFUSING_IDENTIFIER_NAMING] it's a bad name for identifier: better name is: obj, dgt (cannot be auto-corrected){{.*}} +// ;warn:79:5: [IDENTIFIER_LENGTH] identifier's length is incorrect, it should be in range of [2, 64] symbols: D (cannot be auto-corrected){{.*}} enum class IssueType3 { A, B, @@ -60,6 +80,9 @@ enum class IssueType3 { ; } +// ;warn:83:1: [MISSING_KDOC_TOP_LEVEL] all public and internal top-level classes and functions should have Kdoc: Foo (cannot be auto-corrected){{.*}} +// ;warn:88:8: [KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS] in KDoc there should be exactly one empty line after special tags: @implNote{{.*}} +// ;warn:90:23: [EMPTY_BLOCK_STRUCTURE_ERROR] incorrect format of empty block: empty blocks are forbidden unless it is function with override keyword (cannot be auto-corrected){{.*}} class Foo { /** * @implNote lorem ipsum @@ -67,7 +90,10 @@ class Foo { private fun foo() {} } +// ;warn:93:1: [MISSING_KDOC_TOP_LEVEL] all public and internal top-level classes and functions should have Kdoc: mains (cannot be auto-corrected){{.*}} +// ;warn:93:1: [MISSING_KDOC_ON_FUNCTION] all public, internal and protected functions should have Kdoc with proper tags: mains (cannot be auto-corrected){{.*}} fun mains() { + // ;warn:101:23: [MAGIC_NUMBER] avoid using magic numbers, instead define constants with clear names describing what the magic number means: 100 (cannot be auto-corrected){{.*}} val httpClient = HttpClient("myConnection") .apply { url = "http://example.com" diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example4Expected.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example4Expected.kt index 45294bc5e7..4d992d7d7d 100644 --- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example4Expected.kt +++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example4Expected.kt @@ -1,5 +1,8 @@ +// ;warn:1:1: [FILE_NAME_MATCH_CLASS] file name is incorrect - it should match with the class described in it if there is the only one class declared: Example4Expected.kt vs SpecialTagsInKdoc{{.*}} package org.cqfn.diktat +// ;warn:4:1: [MISSING_KDOC_TOP_LEVEL] all public and internal top-level classes and functions should have Kdoc: SpecialTagsInKdoc (cannot be auto-corrected){{.*}} +// ;warn:15:8: [KDOC_NO_EMPTY_TAGS] no empty descriptions in tag blocks are allowed: @return (cannot be auto-corrected){{.*}} class SpecialTagsInKdoc { /** * Empty function to test KDocs @@ -14,6 +17,9 @@ class SpecialTagsInKdoc { fun test() = Unit } +// ;warn:20:1: [MISSING_KDOC_TOP_LEVEL] all public and internal top-level classes and functions should have Kdoc: `method name incorrect, part 4` (cannot be auto-corrected){{.*}} +// ;warn:20:1: [MISSING_KDOC_ON_FUNCTION] all public, internal and protected functions should have Kdoc with proper tags: `method name incorrect, part 4` (cannot be auto-corrected){{.*}} +// ;warn:23:5: [BACKTICKS_PROHIBITED] backticks should not be used in identifier's naming. The only exception test methods marked with @Test annotation: `method name incorrect, part 4` (cannot be auto-corrected){{.*}} fun `method name incorrect, part 4`() { val code = """ class TestPackageName { @@ -32,6 +38,9 @@ fun `method name incorrect, part 4`() { .foo() } +// ;warn:41:1: [MISSING_KDOC_TOP_LEVEL] all public and internal top-level classes and functions should have Kdoc: foo (cannot be auto-corrected){{.*}} +// ;warn:41:1: [MISSING_KDOC_ON_FUNCTION] all public, internal and protected functions should have Kdoc with proper tags: foo (cannot be auto-corrected){{.*}} +// ;warn:41:1: [WRONG_OVERLOADING_FUNCTION_ARGUMENTS] use default argument instead of function overloading: foo (cannot be auto-corrected){{.*}} fun foo() { foo( 0, @@ -43,6 +52,8 @@ fun foo() { ) } +// ;warn:55:1: [MISSING_KDOC_TOP_LEVEL] all public and internal top-level classes and functions should have Kdoc: bar (cannot be auto-corrected){{.*}} +// ;warn:55:1: [MISSING_KDOC_ON_FUNCTION] all public, internal and protected functions should have Kdoc with proper tags: bar (cannot be auto-corrected){{.*}} fun bar() { val diktatExtension = project.extensions.create(DIKTAT_EXTENSION, DiktatExtension::class.java).apply { inputs = project.fileTree("src").apply { @@ -52,6 +63,9 @@ fun bar() { } } +// ;warn:66:1: [MISSING_KDOC_TOP_LEVEL] all public and internal top-level classes and functions should have Kdoc: foo (cannot be auto-corrected){{.*}} +// ;warn:66:1: [MISSING_KDOC_ON_FUNCTION] all public, internal and protected functions should have Kdoc with proper tags: foo (cannot be auto-corrected){{.*}} +// ;warn:66:1: [WRONG_OVERLOADING_FUNCTION_ARGUMENTS] use default argument instead of function overloading: foo (cannot be auto-corrected){{.*}} @Suppress("") fun foo() { val y = "akgjsaujtmaksdkfasakgjsaujtmaksdkfasakgjsaujtmaksdkfasakgjsaujtm" + diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example5Expected.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example5Expected.kt index 8258014d7a..c8d48da285 100644 --- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example5Expected.kt +++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example5Expected.kt @@ -1,3 +1,4 @@ +// ;warn:1:1: [FILE_NAME_MATCH_CLASS] file name is incorrect - it should match with the class described in it if there is the only one class declared: Example5Expected.kt vs Some{{.*}} /* Copyright 2018-2022 John Doe. Licensed under the Apache License, Version 2.0 (the "License"); @@ -9,6 +10,9 @@ package org.cqfn.diktat +// ;warn:13:1: [MISSING_KDOC_TOP_LEVEL] all public and internal top-level classes and functions should have Kdoc: Some (cannot be auto-corrected){{.*}} +// ;warn:13:1: [USE_DATA_CLASS] this class can be converted to a data class: Some (cannot be auto-corrected){{.*}} class Some { + // ;warn:16:5: [MISSING_KDOC_CLASS_ELEMENTS] all public, internal and protected classes, functions and variables inside the class should have Kdoc: config (cannot be auto-corrected){{.*}} val config = Config() } diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example6Expected.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example6Expected.kt index 773fd9b1b6..0162df0fea 100644 --- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example6Expected.kt +++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example6Expected.kt @@ -1,3 +1,4 @@ +// ;warn:1:1: [HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE] files that contain multiple or no classes should contain description of what is inside of this file: there are 0 declared classes and/or objects (cannot be auto-corrected){{.*}} package org.cqfn.diktat val foo = @@ -15,7 +16,7 @@ val bar = val text = """ - x + x """ val dockerFileAsText = diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example6Test.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example6Test.kt index 86cd15e232..928041b459 100644 --- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example6Test.kt +++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example6Test.kt @@ -15,7 +15,7 @@ val bar = val text = """ - x + x """ val dockerFileAsText = diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example7Expected.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example7Expected.kt index 6de69b3349..141a5cb3e4 100644 --- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example7Expected.kt +++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example7Expected.kt @@ -1,5 +1,8 @@ +// ;warn:1:1: [HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE] files that contain multiple or no classes should contain description of what is inside of this file: there are 0 declared classes and/or objects (cannot be auto-corrected){{.*}} package org.cqfn.diktat +// ;warn:4:1: [MISSING_KDOC_TOP_LEVEL] all public and internal top-level classes and functions should have Kdoc: foo (cannot be auto-corrected){{.*}} +// ;warn:4:1: [MISSING_KDOC_ON_FUNCTION] all public, internal and protected functions should have Kdoc with proper tags: foo (cannot be auto-corrected){{.*}} fun foo() { val prop: Int = 0 @@ -18,6 +21,8 @@ fun foo() { } ?: doSmth() } +// ;warn:24:1: [MISSING_KDOC_TOP_LEVEL] all public and internal top-level classes and functions should have Kdoc: fooo (cannot be auto-corrected){{.*}} +// ;warn:24:1: [MISSING_KDOC_ON_FUNCTION] all public, internal and protected functions should have Kdoc with proper tags: fooo (cannot be auto-corrected){{.*}} fun fooo() { if (a) { bar() diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example8Expected.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example8Expected.kt index 2725173aa7..f794a0a780 100644 --- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example8Expected.kt +++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example8Expected.kt @@ -1,14 +1,22 @@ +// ;warn:1:1: [HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE] files that contain multiple or no classes should contain description of what is inside of this file: there are 0 declared classes and/or objects (cannot be auto-corrected) (diktat-ruleset:header-comment) package org.cqfn.diktat +// ;warn:4:1: [MISSING_KDOC_TOP_LEVEL] all public and internal top-level classes and functions should have Kdoc: foo (cannot be auto-corrected) (diktat-ruleset:kdoc-comments) +// ;warn:4:1: [MISSING_KDOC_ON_FUNCTION] all public, internal and protected functions should have Kdoc with proper tags: foo (cannot be auto-corrected) (diktat-ruleset:kdoc-methods) fun foo() { + // ;warn:8:28: [WRONG_WHITESPACE] incorrect usage of whitespaces for code separation: , should have 0 space(s) before and 1 space(s) after, but has 0 space(s) after (diktat-ruleset:horizontal-whitespace) val sum: (Int, Int, Int,) -> Int = fun( x, y, z ): Int = x + y + x + // ;warn:14:5: [DEBUG_PRINT] use a dedicated logging library: found println() (cannot be auto-corrected) (diktat-ruleset:debug-print) println(sum(8, 8, 8)) } +// ;warn:17:1: [MISSING_KDOC_TOP_LEVEL] all public and internal top-level classes and functions should have Kdoc: boo (cannot be auto-corrected) (diktat-ruleset:kdoc-comments) +// ;warn:17:1: [MISSING_KDOC_ON_FUNCTION] all public, internal and protected functions should have Kdoc with proper tags: boo (cannot be auto-corrected) (diktat-ruleset:kdoc-methods) fun boo() { + // ;warn:21:27: [DEBUG_PRINT] use a dedicated logging library: found println() (cannot be auto-corrected) (diktat-ruleset:debug-print) val message = fun() = println("Hello") -} \ No newline at end of file +} diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/KdocFormattingMultilineTagsExpected.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/KdocFormattingMultilineTagsExpected.kt index 16e23537b6..9369f1e970 100644 --- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/KdocFormattingMultilineTagsExpected.kt +++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/KdocFormattingMultilineTagsExpected.kt @@ -1,3 +1,6 @@ +// ;warn:1:1: [HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE] files that contain multiple or no classes should contain description of what is inside of this file: there are 0 declared classes and/or objects (cannot be auto-corrected) (diktat-ruleset:header-comment) +// ;warn:10:4: [KDOC_NO_EMPTY_TAGS] no empty descriptions in tag blocks are allowed: @return (cannot be auto-corrected) (diktat-ruleset:kdoc-formatting) +// ;warn:20:4: [KDOC_NO_EMPTY_TAGS] no empty descriptions in tag blocks are allowed: @return (cannot be auto-corrected) (diktat-ruleset:kdoc-formatting) package org.cqfn.diktat /** diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/LocalVariableWithOffsetExpected.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/LocalVariableWithOffsetExpected.kt index d184f0e8e0..0365c27e63 100644 --- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/LocalVariableWithOffsetExpected.kt +++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/LocalVariableWithOffsetExpected.kt @@ -1,5 +1,7 @@ +// ;warn:1:1: [HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE] files that contain multiple or no classes should contain description of what is inside of this file: there are 0 declared classes and/or objects (cannot be auto-corrected) (diktat-ruleset:header-comment) package org.cqfn.diktat +// ;warn:4:1: [MISSING_KDOC_TOP_LEVEL] all public and internal top-level classes and functions should have Kdoc: boo (cannot be auto-corrected) (diktat-ruleset:kdoc-comments) override fun boo() { val listTestResult: MutableList = mutableListOf() files.chunked(warnPluginConfig.batchSize ?: 1).map { chunk -> diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/save.toml b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/save.toml new file mode 100644 index 0000000000..8c86f032f7 --- /dev/null +++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/save.toml @@ -0,0 +1,14 @@ +[general] +execCmd="java -jar ktlint -R diktat.jar" +tags = ["smokeTest"] +description = "SmokeTest" +suiteName = "SmokeTest" +language = "Kotlin" +expectedWarningsPattern = "// ;warn:?(.*):(\\d*): (.+)" + +["fix and warn"] + ["fix and warn".fix] + execFlags="--disabled_rules=standard -F" + ["fix and warn".warn] + actualWarningsPattern = "(\\w+\\..+):(\\d+):(\\d+): (\\[.*\\].*)$" + exactWarningsMatch = false diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/script/PackageInScriptExpected.kts b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/script/PackageInScriptExpected.kts index 0eb929bd91..e18be85d40 100644 --- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/script/PackageInScriptExpected.kts +++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/script/PackageInScriptExpected.kts @@ -1,19 +1,26 @@ +// ;warn:1:1: [HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE] files that contain multiple or no classes should contain description of what is inside of this file: there are 0 declared classes and/or objects (cannot be auto-corrected) (diktat-ruleset:header-comment) package org.cqfn.diktat.script run { + // ;warn:6:5: [DEBUG_PRINT] use a dedicated logging library: found println() (cannot be auto-corrected) (diktat-ruleset:debug-print) println("hello world!") } +// ;warn:9:1: [MISSING_KDOC_ON_FUNCTION] all public, internal and protected functions should have Kdoc with proper tags: foo (cannot be auto-corrected) (diktat-ruleset:kdoc-methods) fun foo() { + // ;warn:12:5: [DEBUG_PRINT] use a dedicated logging library: found println() (cannot be auto-corrected) (diktat-ruleset:debug-print) println() } +// ;warn:16:5: [IDENTIFIER_LENGTH] identifier's length is incorrect, it should be in range of [2, 64] symbols: q (cannot be auto-corrected) (diktat-ruleset:identifier-naming) val q = Config() run { + // ;warn:20:5: [DEBUG_PRINT] use a dedicated logging library: found println() (cannot be auto-corrected) (diktat-ruleset:debug-print) println("a") } also { + // ;warn:25:5: [DEBUG_PRINT] use a dedicated logging library: found println() (cannot be auto-corrected) (diktat-ruleset:debug-print) println("a") } diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/script/SimpleRunInScriptExpected.kts b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/script/SimpleRunInScriptExpected.kts index 3601b047ed..c1580d6fc3 100644 --- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/script/SimpleRunInScriptExpected.kts +++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/script/SimpleRunInScriptExpected.kts @@ -1,18 +1,25 @@ +// ;warn:1:1: [HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE] files that contain multiple or no classes should contain description of what is inside of this file: there are 0 declared classes and/or objects (cannot be auto-corrected) (diktat-ruleset:header-comment) run { + // ;warn:5:5: [DEBUG_PRINT] use a dedicated logging library: found println() (cannot be auto-corrected) (diktat-ruleset:debug-print) println("hello world!") } +// ;warn:8:1: [MISSING_KDOC_ON_FUNCTION] all public, internal and protected functions should have Kdoc with proper tags: foo (cannot be auto-corrected) (diktat-ruleset:kdoc-methods) fun foo() { + // ;warn:11:5: [DEBUG_PRINT] use a dedicated logging library: found println() (cannot be auto-corrected) (diktat-ruleset:debug-print) println() } +// ;warn:15:5: [IDENTIFIER_LENGTH] identifier's length is incorrect, it should be in range of [2, 64] symbols: q (cannot be auto-corrected) (diktat-ruleset:identifier-naming) val q = Config() run { + // ;warn:19:5: [DEBUG_PRINT] use a dedicated logging library: found println() (cannot be auto-corrected) (diktat-ruleset:debug-print) println("a") } also { + // ;warn:24:5: [DEBUG_PRINT] use a dedicated logging library: found println() (cannot be auto-corrected) (diktat-ruleset:debug-print) println("a") }