Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance smoke tests #1766

Merged
merged 12 commits into from
Oct 27, 2023
Original file line number Diff line number Diff line change
@@ -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,15 +64,15 @@ 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
@Tags(Tag(WarningNames.HEADER_MISSING_OR_WRONG_COPYRIGHT), Tag(WarningNames.HEADER_WRONG_FORMAT))
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) },
)
}

Original file line number Diff line number Diff line change
@@ -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<RulesConfig>? = 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,
Original file line number Diff line number Diff line change
@@ -43,7 +43,7 @@ class DiktatSaveSmokeTest : DiktatSmokeTestBase() {
override fun assertUnfixedLintErrors(diktatErrorConsumer: (List<DiktatError>) -> 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"
@@ -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")
Original file line number Diff line number Diff line change
@@ -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 = {
Original file line number Diff line number Diff line change
@@ -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<File> = 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()
}
}
}
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ import kotlin.io.path.writeText
/**
* A base interface to read resources for testing purposes
*/
interface ResourceReader : Function1<String, Path?> {
fun interface ResourceReader : Function1<String, Path?> {
/**
* @param resourceName
* @return [Path] for provider [resourceName]
@@ -24,8 +24,10 @@ interface ResourceReader : Function1<String, Path?> {
/**
* 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<String, Path?> {
* @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<String, String>,
): 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<String, Path?> {
}
}

/**
* @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, String>): String = replacements.entries
.fold(this) { result, replacement ->
result.replace(replacement.key, replacement.value)
Original file line number Diff line number Diff line change
@@ -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,25 +36,26 @@ 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
*/
@Suppress("FUNCTION_BOOLEAN_PREFIX")
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(