Skip to content

Commit

Permalink
Add unit tests for mis-indented expression body functions (#1363)
Browse files Browse the repository at this point in the history
### What's done:

 * Added unit tests for mis-indented expression body functions.
 * Comparison failures now recognized by IDEA.
 * Content comparison deltas prevented from being wrapped when presented to the user
   (`columnWidth` increased from 80 to `Int.MAX_VALUE`, so deltas will no longer have any inline `<br/>`).
 * Failing tests (caused by #1347, skipped by default) can be re-enabled by running with `-Dtests.can.be.muted=true`
 * See #1330.
  • Loading branch information
0x6675636b796f75676974687562 authored Jun 15, 2022
1 parent 3dcd13e commit 0959a2b
Show file tree
Hide file tree
Showing 9 changed files with 731 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@ import org.cqfn.diktat.ruleset.rules.chapter3.files.IndentationRule
import org.cqfn.diktat.util.FixTestBase

import generated.WarningNames
import org.assertj.core.api.SoftAssertions.assertSoftly
import org.intellij.lang.annotations.Language
import org.junit.jupiter.api.Assumptions.assumeTrue
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir

import java.nio.file.Path

class IndentationRuleFixTest : FixTestBase("test/paragraph3/indentation",
::IndentationRule,
Expand All @@ -22,7 +28,7 @@ class IndentationRuleFixTest : FixTestBase("test/paragraph3/indentation",
)
)
)
) {
), IndentationRuleTestMixin {
@Test
@Tag(WarningNames.WRONG_INDENTATION)
fun `parameters should be properly aligned`() {
Expand All @@ -46,4 +52,126 @@ class IndentationRuleFixTest : FixTestBase("test/paragraph3/indentation",
fun `regression - incorrect fixing in constructor parameter list`() {
fixAndCompare("ConstructorExpected.kt", "ConstructorTest.kt")
}

/**
* This test has a counterpart under [IndentationRuleWarnTest].
*
* See [#1330](https://github.com/saveourtool/diktat/issues/1330).
*/
@Test
@Tag(WarningNames.WRONG_INDENTATION)
fun `expression body functions should remain unchanged if properly indented (extendedIndentAfterOperators = true)`(@TempDir tempDir: Path) {
val defaultConfig = IndentationConfig("newlineAtEnd" to false)
val customConfig = defaultConfig.withCustomParameters("extendedIndentAfterOperators" to true)

lintMultipleMethods(
expressionBodyFunctionsContinuationIndent,
tempDir = tempDir,
rulesConfigList = customConfig.asRulesConfigList())
}

/**
* This test has a counterpart under [IndentationRuleWarnTest].
*
* See [#1330](https://github.com/saveourtool/diktat/issues/1330).
*/
@Test
@Tag(WarningNames.WRONG_INDENTATION)
fun `expression body functions should remain unchanged if properly indented (extendedIndentAfterOperators = false)`(@TempDir tempDir: Path) {
val defaultConfig = IndentationConfig("newlineAtEnd" to false)
val customConfig = defaultConfig.withCustomParameters("extendedIndentAfterOperators" to false)

lintMultipleMethods(
expressionBodyFunctionsSingleIndent,
tempDir = tempDir,
rulesConfigList = customConfig.asRulesConfigList())
}

/**
* This test has a counterpart under [IndentationRuleWarnTest].
*
* See [#1330](https://github.com/saveourtool/diktat/issues/1330).
*/
@Test
@Tag(WarningNames.WRONG_INDENTATION)
fun `expression body functions should be reformatted if mis-indented (extendedIndentAfterOperators = true)`(@TempDir tempDir: Path) {
assumeTrue(testsCanBeMuted()) {
"Skipping a known-to-fail test"
}

val defaultConfig = IndentationConfig("newlineAtEnd" to false)
val customConfig = defaultConfig.withCustomParameters("extendedIndentAfterOperators" to true)

lintMultipleMethods(
actualContent = expressionBodyFunctionsSingleIndent,
expectedContent = expressionBodyFunctionsContinuationIndent,
tempDir = tempDir,
rulesConfigList = customConfig.asRulesConfigList())
}

/**
* This test has a counterpart under [IndentationRuleWarnTest].
*
* See [#1330](https://github.com/saveourtool/diktat/issues/1330).
*/
@Test
@Tag(WarningNames.WRONG_INDENTATION)
fun `expression body functions should be reformatted if mis-indented (extendedIndentAfterOperators = false)`(@TempDir tempDir: Path) {
assumeTrue(testsCanBeMuted()) {
"Skipping a known-to-fail test"
}

val defaultConfig = IndentationConfig("newlineAtEnd" to false)
val customConfig = defaultConfig.withCustomParameters("extendedIndentAfterOperators" to false)

lintMultipleMethods(
actualContent = expressionBodyFunctionsContinuationIndent,
expectedContent = expressionBodyFunctionsSingleIndent,
tempDir = tempDir,
rulesConfigList = customConfig.asRulesConfigList())
}

/**
* @param actualContent the original file content (may well be modified as
* fixes are applied).
* @param expectedContent the content the file is expected to have after the
* fixes are applied.
*/
private fun lintMultipleMethods(
@Language("kotlin") actualContent: Array<String>,
@Language("kotlin") expectedContent: Array<String> = actualContent,
tempDir: Path,
rulesConfigList: List<RulesConfig>? = null
) {
require(actualContent.isNotEmpty()) {
"code fragments is an empty array"
}

require(actualContent.size == expectedContent.size) {
"The actual and expected code fragments are arrays of different size: ${actualContent.size} != ${expectedContent.size}"
}

assertSoftly { softly ->
sequence {
yieldAll(actualContent.asSequence() zip expectedContent.asSequence())

/*
* All fragments concatenated.
*/
yield(actualContent.concatenated() to expectedContent.concatenated())
}.forEach { (actual, expected) ->
val lintResult = fixAndCompareContent(
actual,
expected,
tempDir,
rulesConfigList)

if (!lintResult.isSuccessful) {
softly.assertThat(lintResult.actualContent)
.describedAs("lint result for \"$actual\"")
.isEqualTo(lintResult.expectedContent)
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package org.cqfn.diktat.ruleset.chapter3.spaces

import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_INDENTATION
import org.cqfn.diktat.ruleset.utils.indentation.IndentationConfig
import org.intellij.lang.annotations.Language

import java.lang.Boolean.getBoolean as getBooleanProperty

/**
* Code shared by [IndentationRuleWarnTest] and [IndentationRuleFixTest].
*
* @see IndentationRuleWarnTest
* @see IndentationRuleFixTest
*/
internal interface IndentationRuleTestMixin {
/**
* See [#1330](https://github.com/saveourtool/diktat/issues/1330).
*
* @see expressionBodyFunctionsContinuationIndent
*/
@Suppress("CUSTOM_GETTERS_SETTERS")
val expressionBodyFunctionsSingleIndent: Array<String>
@Language("kotlin")
get() =
arrayOf(
"""
|@Test
|fun `checking that suppression with ignore everything works`() {
| val code =
| ""${'"'}
| @Suppress("diktat")
| fun foo() {
| val a = 1
| }
| ""${'"'}.trimIndent()
| lintMethod(code)
|}
""".trimMargin(),

"""
|val currentTime: Time
| get() =
| with(currentDateTime) {
| Time(hour = hour, minute = minute, second = second)
| }
""".trimMargin(),

"""
|fun formatDateByPattern(date: String, pattern: String = "ddMMyy"): String =
| DateTimeFormatter.ofPattern(pattern).format(LocalDate.parse(date))
""".trimMargin(),

"""
|private fun createLayoutParams(): WindowManager.LayoutParams =
| WindowManager.LayoutParams().apply { /* ... */ }
""".trimMargin(),

"""
|val offsetDelta =
| if (shimmerAnimationType != ShimmerAnimationType.FADE) translateAnim.dp
| else 2000.dp
""".trimMargin(),

"""
|private fun lerp(start: Float, stop: Float, fraction: Float): Float =
| (1 - fraction) * start + fraction * stop
""".trimMargin()
)

/**
* See [#1330](https://github.com/saveourtool/diktat/issues/1330).
*
* @see expressionBodyFunctionsSingleIndent
*/
@Suppress("CUSTOM_GETTERS_SETTERS")
val expressionBodyFunctionsContinuationIndent: Array<String>
@Language("kotlin")
get() =
arrayOf(
"""
|@Test
|fun `checking that suppression with ignore everything works`() {
| val code =
| ""${'"'}
| @Suppress("diktat")
| fun foo() {
| val a = 1
| }
| ""${'"'}.trimIndent()
| lintMethod(code)
|}
""".trimMargin(),

"""
|val currentTime: Time
| get() =
| with(currentDateTime) {
| Time(hour = hour, minute = minute, second = second)
| }
""".trimMargin(),

"""
|fun formatDateByPattern(date: String, pattern: String = "ddMMyy"): String =
| DateTimeFormatter.ofPattern(pattern).format(LocalDate.parse(date))
""".trimMargin(),

"""
|private fun createLayoutParams(): WindowManager.LayoutParams =
| WindowManager.LayoutParams().apply { /* ... */ }
""".trimMargin(),

"""
|val offsetDelta =
| if (shimmerAnimationType != ShimmerAnimationType.FADE) translateAnim.dp
| else 2000.dp
""".trimMargin(),

"""
|private fun lerp(start: Float, stop: Float, fraction: Float): Float =
| (1 - fraction) * start + fraction * stop
""".trimMargin(),
)

/**
* Creates an `IndentationConfig` from zero or more
* [config entries][configEntries]. Invoke without arguments to create a
* default `IndentationConfig`.
*
* @param configEntries the configuration entries to create this instance from.
* @see [IndentationConfig]
*/
@Suppress("TestFunctionName", "FUNCTION_NAME_INCORRECT_CASE")
fun IndentationConfig(vararg configEntries: Pair<String, Any>): IndentationConfig =
IndentationConfig(mapOf(*configEntries).mapValues(Any::toString))

/**
* @param configEntries the optional values which override the state of this
* [IndentationConfig].
* @return the content of this [IndentationConfig] as a map, with some
* configuration entries overridden via [configEntries].
*/
@Suppress("STRING_TEMPLATE_QUOTES")
fun IndentationConfig.withCustomParameters(vararg configEntries: Pair<String, Any>): Map<String, String> =
mutableMapOf(
"alignedParameters" to "$alignedParameters",
"indentationSize" to "$indentationSize",
"newlineAtEnd" to "$newlineAtEnd",
"extendedIndentOfParameters" to "$extendedIndentOfParameters",
"extendedIndentAfterOperators" to "$extendedIndentAfterOperators",
"extendedIndentBeforeDot" to "$extendedIndentBeforeDot",
).apply {
configEntries.forEach { (key, value) ->
this[key] = value.toString()
}
}

/**
* Converts this map to a list containing a single [RulesConfig].
*
* @return the list containing a single [RulesConfig] entry.
*/
fun Map<String, String>.asRulesConfigList(): List<RulesConfig> =
listOf(
RulesConfig(
name = WRONG_INDENTATION.name,
enabled = true,
configuration = this
)
)

/**
* @return the concatenated content of this array (elements separated with
* blank lines).
*/
fun Array<String>.concatenated(): String =
joinToString(separator = "\n\n")

/**
* @return `true` if known-to-fail unit tests can be muted on the CI server.
*/
@Suppress("FUNCTION_BOOLEAN_PREFIX")
fun testsCanBeMuted(): Boolean =
getBooleanProperty("tests.can.be.muted")
}
Loading

0 comments on commit 0959a2b

Please sign in to comment.