From f8fd40f73b90040abd8a48ddc8e7ab262fffb145 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Fri, 27 Oct 2023 16:25:01 +0300 Subject: [PATCH] Enhance smoke tests (#1766) - added copying test data --- .../chapter2/HeaderCommentRuleFixTest.kt | 28 ++++----- .../saveourtool/diktat/util/FixTestBase.kt | 6 +- .../ruleset/smoke/DiktatSaveSmokeTest.kt | 13 ++-- .../diktat/ruleset/smoke/DiktatSmokeTest.kt | 16 +++-- .../ruleset/smoke/DiktatSmokeTestBase.kt | 61 +++++++++++-------- .../framework/processing/ResourceReader.kt | 22 +++++-- .../processing/TestComparatorUnit.kt | 26 +++++--- 7 files changed, 104 insertions(+), 68 deletions(-) diff --git a/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter2/HeaderCommentRuleFixTest.kt b/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter2/HeaderCommentRuleFixTest.kt index eddec48bd0..77ae0de187 100644 --- a/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter2/HeaderCommentRuleFixTest.kt +++ b/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter2/HeaderCommentRuleFixTest.kt @@ -4,7 +4,7 @@ import com.saveourtool.diktat.common.config.rules.RulesConfig import com.saveourtool.diktat.ruleset.constants.Warnings.HEADER_MISSING_OR_WRONG_COPYRIGHT import com.saveourtool.diktat.ruleset.constants.Warnings.HEADER_WRONG_FORMAT import com.saveourtool.diktat.ruleset.rules.chapter2.comments.HeaderCommentRule -import com.saveourtool.diktat.test.framework.processing.ResourceReader +import com.saveourtool.diktat.test.framework.processing.ResourceReader.Companion.withReplacements import com.saveourtool.diktat.util.FixTestBase import generated.WarningNames @@ -31,13 +31,13 @@ class HeaderCommentRuleFixTest : FixTestBase( @Test @Tag(WarningNames.HEADER_WRONG_FORMAT) fun `new line should be inserted after header KDoc`(@TempDir tempDir: Path) { - fixAndCompare("NewlineAfterHeaderKdocExpected.kt", "NewlineAfterHeaderKdocTest.kt", resourceReader = ResourceReader.withReplacements(tempDir, currentYearReplacement)) + fixAndCompare("NewlineAfterHeaderKdocExpected.kt", "NewlineAfterHeaderKdocTest.kt", overrideResourceReader = { it.withReplacements(tempDir, currentYearReplacement) }) } @Test @Tag(WarningNames.HEADER_MISSING_OR_WRONG_COPYRIGHT) fun `if no copyright is present and mandatoryCopyright=true, it is added`(@TempDir tempDir: Path) { - fixAndCompare("AutoCopyrightExpected.kt", "AutoCopyrightTest.kt", resourceReader = ResourceReader.withReplacements(tempDir, currentYearReplacement)) + fixAndCompare("AutoCopyrightExpected.kt", "AutoCopyrightTest.kt", overrideResourceReader = { it.withReplacements(tempDir, currentYearReplacement) }) } @Test @@ -54,7 +54,7 @@ class HeaderCommentRuleFixTest : FixTestBase( ), RulesConfig(HEADER_WRONG_FORMAT.name, true, emptyMap()) ), - resourceReader = ResourceReader.withReplacements(tempDir, currentYearReplacement), + overrideResourceReader = { it.withReplacements(tempDir, currentYearReplacement) }, ) } @@ -64,7 +64,7 @@ class HeaderCommentRuleFixTest : FixTestBase( @Test @Tag(WarningNames.HEADER_NOT_BEFORE_PACKAGE) fun `header KDoc should be moved before package`(@TempDir tempDir: Path) { - fixAndCompare("MisplacedHeaderKdocExpected.kt", "MisplacedHeaderKdocTest.kt", resourceReader = ResourceReader.withReplacements(tempDir, currentYearReplacement)) + fixAndCompare("MisplacedHeaderKdocExpected.kt", "MisplacedHeaderKdocTest.kt", overrideResourceReader = { it.withReplacements(tempDir, currentYearReplacement) }) } @Test @@ -72,7 +72,7 @@ class HeaderCommentRuleFixTest : FixTestBase( fun `header KDoc should be moved before package - no copyright`(@TempDir tempDir: Path) { fixAndCompare("MisplacedHeaderKdocNoCopyrightExpected.kt", "MisplacedHeaderKdocNoCopyrightTest.kt", listOf(RulesConfig(HEADER_MISSING_OR_WRONG_COPYRIGHT.name, false, emptyMap()), RulesConfig(HEADER_WRONG_FORMAT.name, true, emptyMap())), - resourceReader = ResourceReader.withReplacements(tempDir, currentYearReplacement), + overrideResourceReader = { it.withReplacements(tempDir, currentYearReplacement) }, ) } @@ -82,7 +82,7 @@ class HeaderCommentRuleFixTest : FixTestBase( fixAndCompare( "MisplacedHeaderKdocAppendedCopyrightExpected.kt", "MisplacedHeaderKdocAppendedCopyrightTest.kt", - resourceReader = ResourceReader.withReplacements(tempDir, currentYearReplacement), + overrideResourceReader = { it.withReplacements(tempDir, currentYearReplacement) }, ) } @@ -94,7 +94,7 @@ class HeaderCommentRuleFixTest : FixTestBase( "isCopyrightMandatory" to "true", "copyrightText" to "Copyright (c) My Company., Ltd. 2012-2019. All rights reserved." ))), - resourceReader = ResourceReader.withReplacements(tempDir, currentYearReplacement), + overrideResourceReader = { it.withReplacements(tempDir, currentYearReplacement) }, ) } @@ -106,7 +106,7 @@ class HeaderCommentRuleFixTest : FixTestBase( "isCopyrightMandatory" to "true", "copyrightText" to "Copyright (c) My Company., Ltd. 2021. All rights reserved." ))), - resourceReader = ResourceReader.withReplacements(tempDir, currentYearReplacement), + overrideResourceReader = { it.withReplacements(tempDir, currentYearReplacement) }, ) } @@ -118,7 +118,7 @@ class HeaderCommentRuleFixTest : FixTestBase( "isCopyrightMandatory" to "true", "copyrightText" to "Copyright (c) My Company., Ltd. 2012-2019. All rights reserved." ))), - resourceReader = ResourceReader.withReplacements(tempDir, currentYearReplacement), + overrideResourceReader = { it.withReplacements(tempDir, currentYearReplacement) }, ) } @@ -130,7 +130,7 @@ class HeaderCommentRuleFixTest : FixTestBase( "isCopyrightMandatory" to "true", "copyrightText" to "Copyright (c) My Company., Ltd. 2012-2019. All rights reserved." ))), - resourceReader = ResourceReader.withReplacements(tempDir, currentYearReplacement), + overrideResourceReader = { it.withReplacements(tempDir, currentYearReplacement) }, ) } @@ -142,7 +142,7 @@ class HeaderCommentRuleFixTest : FixTestBase( "isCopyrightMandatory" to "true", "copyrightText" to "Copyright (c) My Company., Ltd. 2012-2021. All rights reserved." ))), - resourceReader = ResourceReader.withReplacements(tempDir, currentYearReplacement), + overrideResourceReader = { it.withReplacements(tempDir, currentYearReplacement) }, ) } @@ -168,7 +168,7 @@ class HeaderCommentRuleFixTest : FixTestBase( | limitations under the License. """.trimMargin() ))), - resourceReader = ResourceReader.withReplacements(tempDir, currentYearReplacement), + overrideResourceReader = { it.withReplacements(tempDir, currentYearReplacement) }, ) } @@ -186,7 +186,7 @@ class HeaderCommentRuleFixTest : FixTestBase( | You may obtain a copy of the License at """.trimMargin() ))), - resourceReader = ResourceReader.withReplacements(tempDir, currentYearReplacement), + overrideResourceReader = { it.withReplacements(tempDir, currentYearReplacement) }, ) } diff --git a/diktat-rules/src/test/kotlin/com/saveourtool/diktat/util/FixTestBase.kt b/diktat-rules/src/test/kotlin/com/saveourtool/diktat/util/FixTestBase.kt index a5a3c2aaf9..eb07b4e201 100644 --- a/diktat-rules/src/test/kotlin/com/saveourtool/diktat/util/FixTestBase.kt +++ b/diktat-rules/src/test/kotlin/com/saveourtool/diktat/util/FixTestBase.kt @@ -44,18 +44,18 @@ open class FixTestBase( * @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 [defaultRulesConfigList] - * @param resourceReader [ResourceReader] to read resource content. + * @param overrideResourceReader function to override [ResourceReader] to read resource content. * @see fixAndCompareContent */ protected fun fixAndCompare( expectedPath: String, testPath: String, overrideRulesConfigList: List? = null, - resourceReader: ResourceReader = ResourceReader.default, + overrideResourceReader: (ResourceReader) -> ResourceReader = { it }, ) { val testComparatorUnit = testComparatorUnitSupplier(overrideRulesConfigList) val result = testComparatorUnit - .compareFilesFromResources(expectedPath, testPath, resourceReader) + .compareFilesFromResources(expectedPath, testPath, overrideResourceReader) if (!result.isSuccessful) { Assertions.assertEquals( result.expectedContentWithoutWarns, diff --git a/diktat-ruleset/src/test/kotlin/com/saveourtool/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt b/diktat-ruleset/src/test/kotlin/com/saveourtool/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt index e5c6c68af1..2ebc6315c1 100644 --- a/diktat-ruleset/src/test/kotlin/com/saveourtool/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt +++ b/diktat-ruleset/src/test/kotlin/com/saveourtool/diktat/ruleset/smoke/DiktatSaveSmokeTest.kt @@ -43,7 +43,7 @@ class DiktatSaveSmokeTest : DiktatSmokeTestBase() { override fun assertUnfixedLintErrors(diktatErrorConsumer: (List) -> Unit) = Unit /** - * @param testPath path to file with code that will be transformed by formatter, relative to [TestComparatorUnit.resourceFilePath] + * @param testPath path to file with code that will be transformed by formatter, loaded by [TestComparatorUnit.resourceReader] * @param configFilePath path of diktat-analysis file */ @Suppress("TOO_LONG_FUNCTION") @@ -107,13 +107,13 @@ class DiktatSaveSmokeTest : DiktatSmokeTestBase() { } /** - * @param testPath path to file with code that will be transformed by formatter, relative to [TestComparatorUnit.resourceFilePath] + * @param testPath path to file with code that will be transformed by formatter, loaded by [TestComparatorUnit.resourceReader] * @return ProcessBuilder */ private fun createProcessBuilderWithCmd(testPath: String): ProcessBuilder { - val savePath = "$BASE_DIRECTORY/${getSaveForCurrentOs()}" + val savePath = baseDirectoryPath.resolve(getSaveForCurrentOs()).toString() val saveArgs = arrayOf( - "$BASE_DIRECTORY/src/main/kotlin", + baseDirectoryPath.resolve("src/main/kotlin").toString(), testPath, "--log", "all" @@ -121,7 +121,7 @@ class DiktatSaveSmokeTest : DiktatSmokeTestBase() { return when { System.getProperty("os.name").isWindows() -> arrayOf(savePath, *saveArgs) - else -> arrayOf("sh", "-c", "chmod 777 $savePath ; ./$savePath ${saveArgs.joinToString(" ")}") + else -> arrayOf("sh", "-c", "chmod 777 $savePath ; $savePath ${saveArgs.joinToString(" ")}") }.let { args -> ProcessBuilder(*args) } @@ -129,10 +129,9 @@ class DiktatSaveSmokeTest : DiktatSmokeTestBase() { companion object { private val logger = KotlinLogging.logger {} - private const val BASE_DIRECTORY = "src/test/resources/test/smoke" private const val SAVE_VERSION: String = "0.3.4" private const val TEMP_DIRECTORY = ".save-cli" - private val baseDirectoryPath = Path(BASE_DIRECTORY).absolute() + private val baseDirectoryPath = tempDir.absolute() private fun getSaveForCurrentOs(): String { val osName = System.getProperty("os.name") diff --git a/diktat-ruleset/src/test/kotlin/com/saveourtool/diktat/ruleset/smoke/DiktatSmokeTest.kt b/diktat-ruleset/src/test/kotlin/com/saveourtool/diktat/ruleset/smoke/DiktatSmokeTest.kt index 01874baabc..61eb2cf45d 100644 --- a/diktat-ruleset/src/test/kotlin/com/saveourtool/diktat/ruleset/smoke/DiktatSmokeTest.kt +++ b/diktat-ruleset/src/test/kotlin/com/saveourtool/diktat/ruleset/smoke/DiktatSmokeTest.kt @@ -23,11 +23,17 @@ class DiktatSmokeTest : DiktatSmokeTestBase() { expected: String, test: String, ) { - Assertions.assertTrue( - getTestComparatorUnit(config) - .compareFilesFromResources(expected, test) - .isSuccessful + val result = getTestComparatorUnit(config) + .compareFilesFromResources(expected, test) + Assertions.assertAll( + { + Assertions.assertTrue(result.isSuccessful) + }, + { + Assertions.assertEquals(result.expectedContentWithoutWarns, result.actualContent) + } ) + } @BeforeEach @@ -40,7 +46,7 @@ class DiktatSmokeTest : DiktatSmokeTestBase() { } private fun getTestComparatorUnit(config: Path) = TestComparatorUnit( - resourceFilePath = RESOURCE_FILE_PATH, + resourceReader = { tempDir.resolve("src/main/kotlin").resolve(it).normalize() }, function = { testFile -> format( ruleSetSupplier = { diff --git a/diktat-ruleset/src/test/kotlin/com/saveourtool/diktat/ruleset/smoke/DiktatSmokeTestBase.kt b/diktat-ruleset/src/test/kotlin/com/saveourtool/diktat/ruleset/smoke/DiktatSmokeTestBase.kt index 9156ce21a6..f659022ad3 100644 --- a/diktat-ruleset/src/test/kotlin/com/saveourtool/diktat/ruleset/smoke/DiktatSmokeTestBase.kt +++ b/diktat-ruleset/src/test/kotlin/com/saveourtool/diktat/ruleset/smoke/DiktatSmokeTestBase.kt @@ -33,26 +33,33 @@ import com.saveourtool.diktat.ruleset.utils.indentation.IndentationConfig import com.saveourtool.diktat.ruleset.utils.indentation.IndentationConfig.Companion.EXTENDED_INDENT_AFTER_OPERATORS import com.saveourtool.diktat.ruleset.utils.indentation.IndentationConfig.Companion.EXTENDED_INDENT_BEFORE_DOT import com.saveourtool.diktat.ruleset.utils.indentation.IndentationConfig.Companion.EXTENDED_INDENT_FOR_EXPRESSION_BODIES -import com.saveourtool.diktat.test.framework.util.deleteIfExistsSilently import com.charleskorn.kaml.Yaml import com.charleskorn.kaml.YamlConfiguration import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Tag import org.junit.jupiter.api.Test import org.junit.jupiter.api.Timeout -import java.io.File +import org.junit.jupiter.api.io.TempDir import java.nio.file.Path import java.nio.file.Paths import java.time.LocalDate import java.util.concurrent.TimeUnit.SECONDS +import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.PathWalkOption +import kotlin.io.path.copyTo +import kotlin.io.path.createDirectories import kotlin.io.path.createTempFile import kotlin.io.path.inputStream +import kotlin.io.path.isDirectory +import kotlin.io.path.moveTo +import kotlin.io.path.name +import kotlin.io.path.toPath +import kotlin.io.path.walk import kotlin.io.path.writeText import kotlinx.serialization.builtins.ListSerializer @@ -390,36 +397,40 @@ abstract class DiktatSmokeTestBase { companion object { private const val DEFAULT_CONFIG_PATH = "../diktat-analysis.yml" - const val RESOURCE_FILE_PATH = "test/smoke/src/main/kotlin" + private const val ROOT_RESOURCE_FILE_PATH = "test/smoke" private const val TEST_TIMEOUT_SECONDS = 30L - private val tmpFiles: MutableList = mutableListOf() + + @JvmStatic + @TempDir + internal var tempDir: Path = Paths.get("/invalid") @BeforeAll @JvmStatic + @OptIn(ExperimentalPathApi::class) @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) } + val resourceFilePath = DiktatSmokeTestBase::class.java + .classLoader + .getResource(ROOT_RESOURCE_FILE_PATH) + .let { resource -> + requireNotNull(resource) { + "$ROOT_RESOURCE_FILE_PATH not found" + } + } + .toURI() + .toPath() + resourceFilePath.walk(PathWalkOption.INCLUDE_DIRECTORIES).forEach { file -> + if (file.isDirectory()) { + tempDir.resolve(resourceFilePath.relativize(file)).createDirectories() + } else { + val dest = tempDir.resolve(resourceFilePath.relativize(file)) + file.copyTo(dest) + when (file.name) { + "build.gradle.kts_" -> dest.moveTo(dest.parent.resolve("build.gradle.kts")) + "Example1Test.kt" -> dest.copyTo(dest.parent.resolve("Example1-2Test.kt")) + } } - } - @AfterAll - @JvmStatic - internal fun deleteTmpFiles() { - tmpFiles.forEach { - it.toPath().deleteIfExistsSilently() } } } diff --git a/diktat-test-framework/src/main/kotlin/com/saveourtool/diktat/test/framework/processing/ResourceReader.kt b/diktat-test-framework/src/main/kotlin/com/saveourtool/diktat/test/framework/processing/ResourceReader.kt index ccb413c92d..daa5147506 100644 --- a/diktat-test-framework/src/main/kotlin/com/saveourtool/diktat/test/framework/processing/ResourceReader.kt +++ b/diktat-test-framework/src/main/kotlin/com/saveourtool/diktat/test/framework/processing/ResourceReader.kt @@ -11,7 +11,7 @@ import kotlin.io.path.writeText /** * A base interface to read resources for testing purposes */ -interface ResourceReader : Function1 { +fun interface ResourceReader : Function1 { /** * @param resourceName * @return [Path] for provider [resourceName] @@ -24,8 +24,10 @@ interface ResourceReader : Function1 { /** * Default implementation of [ResourceReader] */ - val default: ResourceReader = object : ResourceReader { - override fun invoke(resourceName: String): Path? = javaClass.classLoader.getResource(resourceName) + val default: ResourceReader = ResourceReader { resourceName -> + ResourceReader::class.java + .classLoader + .getResource(resourceName) ?.toURI() ?.toPath() .also { @@ -40,11 +42,11 @@ interface ResourceReader : Function1 { * @param replacements a map of replacements which will be applied to actual and expected content before comparing. * @return Instance of [ResourceReader] with replacements of content */ - fun withReplacements( + fun ResourceReader.withReplacements( tempDir: Path, replacements: Map, - ): ResourceReader = object : ResourceReader { - override fun invoke(resourceName: String): Path? = default.invoke(resourceName) + ): ResourceReader = ResourceReader { resourceName -> + this@withReplacements.invoke(resourceName) ?.let { originalFile -> tempDir.resolve(resourceName) .also { resultFile -> @@ -57,6 +59,14 @@ interface ResourceReader : Function1 { } } + /** + * @param resourceFilePath a prefix for loading resources + * @return Instance of [ResourceReader] which loads resource with [resourceFilePath] as prefix + */ + fun ResourceReader.withPrefix( + resourceFilePath: String, + ): ResourceReader = ResourceReader { resourceName -> this@withPrefix.invoke("$resourceFilePath/$resourceName") } + private fun String.replaceAll(replacements: Map): String = replacements.entries .fold(this) { result, replacement -> result.replace(replacement.key, replacement.value) diff --git a/diktat-test-framework/src/main/kotlin/com/saveourtool/diktat/test/framework/processing/TestComparatorUnit.kt b/diktat-test-framework/src/main/kotlin/com/saveourtool/diktat/test/framework/processing/TestComparatorUnit.kt index 8731fb2033..a598538268 100644 --- a/diktat-test-framework/src/main/kotlin/com/saveourtool/diktat/test/framework/processing/TestComparatorUnit.kt +++ b/diktat-test-framework/src/main/kotlin/com/saveourtool/diktat/test/framework/processing/TestComparatorUnit.kt @@ -1,5 +1,6 @@ package com.saveourtool.diktat.test.framework.processing +import com.saveourtool.diktat.test.framework.processing.ResourceReader.Companion.withPrefix import com.saveourtool.diktat.test.framework.util.readTextOrNull import com.saveourtool.diktat.test.framework.util.toUnixEndLines import io.github.oshai.kotlinlogging.KotlinLogging @@ -10,15 +11,23 @@ import kotlin.io.path.name /** * Class that can apply transformation to an input file and then compare with expected result and output difference. * - * @param resourceFilePath only used when the files are loaded as resources, + * @param resourceReader only used when the files are loaded as resources, * via [compareFilesFromResources]. * @param function a transformation that will be applied to the file */ @Suppress("ForbiddenComment", "TYPE_ALIAS") class TestComparatorUnit( - private val resourceFilePath: String, + private val resourceReader: ResourceReader = ResourceReader.default, private val function: (testFile: Path) -> String, ) { + constructor( + resourceFilePath: String, + function: (testFile: Path) -> String, + ) : this( + resourceReader = ResourceReader.default.withPrefix(resourceFilePath), + function = function, + ) + /** * @param expectedResult the name of the resource which has the expected * content. The trailing newline, if any, **won't be read** as a separate @@ -27,7 +36,7 @@ class TestComparatorUnit( * `newlineAtEnd` is `true`), then the file should end with **two** * consecutive linebreaks. * @param testFileStr the name of the resource which has the original content. - * @param resourceReader [ResourceReader] to read resource content + * @param overrideResourceReader function to override [ResourceReader] to read resource content * @return the result of file comparison by their content. * @see compareFilesFromFileSystem */ @@ -35,17 +44,18 @@ class TestComparatorUnit( fun compareFilesFromResources( expectedResult: String, testFileStr: String, - resourceReader: ResourceReader = ResourceReader.default, + overrideResourceReader: (ResourceReader) -> ResourceReader = { it }, ): FileComparisonResult { - val expectedPath = resourceReader("$resourceFilePath/$expectedResult") - val testPath = resourceReader("$resourceFilePath/$testFileStr") + val overriddenResourceReader = overrideResourceReader(resourceReader) + val expectedPath = overriddenResourceReader(expectedResult) + val testPath = overriddenResourceReader(testFileStr) if (testPath == null || expectedPath == null) { log.error { "Not able to find files for running test: $expectedResult and $testFileStr" } return FileComparisonResult( isSuccessful = false, delta = null, - actualContent = "// $resourceFilePath/$expectedResult is found: ${testPath != null}", - expectedContent = "// $resourceFilePath/$testFileStr is found: ${expectedPath != null}") + actualContent = "// $expectedResult is found: ${testPath != null}", + expectedContent = "// $testFileStr is found: ${expectedPath != null}") } return compareFilesFromFileSystem(