Skip to content

Commit

Permalink
Refactor config printer to improve testability (#4580)
Browse files Browse the repository at this point in the history
Co-authored-by: Markus Schwarz <post@markus-schwarz.net>
  • Loading branch information
marschwar and Markus Schwarz committed Feb 13, 2022
1 parent b6731ed commit 2aad546
Show file tree
Hide file tree
Showing 2 changed files with 225 additions and 18 deletions.
Expand Up @@ -14,35 +14,37 @@ internal fun YamlNode.printRuleSetPage(ruleSetPage: RuleSetPage) {
printRuleSet(ruleSetPage.ruleSet, ruleSetPage.rules)
}

@Suppress("ComplexMethod") // preserving the declarative structure while building the dsl
private fun YamlNode.printRuleSet(ruleSet: RuleSetProvider, rules: List<Rule>) {
internal fun YamlNode.printRuleSet(ruleSet: RuleSetProvider, rules: List<Rule>) {
node(ruleSet.name) {
keyValue { Config.ACTIVE_KEY to "${ruleSet.defaultActivationStatus.active}" }
val ruleSetExclusion = exclusions.singleOrNull { ruleSet.name in it.ruleSets }
if (ruleSetExclusion != null) {
keyValue { Config.EXCLUDES_KEY to ruleSetExclusion.pattern }
}

ruleSet.configuration.forEach { printConfiguration(it) }

rules.forEach { rule ->
node(rule.name) {
keyValue { Config.ACTIVE_KEY to "${rule.defaultActivationStatus.active}" }
if (rule.autoCorrect) {
keyValue { Config.AUTO_CORRECT_KEY to "true" }
}
val ruleExclusion = exclusions.singleOrNull { it.isExcluded(rule) }
if (ruleExclusion != null) {
keyValue { Config.EXCLUDES_KEY to ruleExclusion.pattern }
}
rule.configuration.forEach { printConfiguration(it) }
}
}
ruleSet.configuration.forEach(::printConfiguration)

rules.forEach(::printRule)

emptyLine()
}
}

private fun YamlNode.printConfiguration(configuration: Configuration) {
internal fun YamlNode.printRule(rule: Rule) {
node(rule.name) {
keyValue { Config.ACTIVE_KEY to "${rule.defaultActivationStatus.active}" }
if (rule.autoCorrect) {
keyValue { Config.AUTO_CORRECT_KEY to "true" }
}
val ruleExclusion = exclusions.singleOrNull { it.isExcluded(rule) }
if (ruleExclusion != null) {
keyValue { Config.EXCLUDES_KEY to ruleExclusion.pattern }
}
rule.configuration.forEach(::printConfiguration)
}
}

internal fun YamlNode.printConfiguration(configuration: Configuration) {
if (configuration.isDeprecated()) return

if (configuration.isDefaultValueNonEmptyList()) {
Expand Down
@@ -0,0 +1,205 @@
package io.gitlab.arturbosch.detekt.generator.printer.defaultconfig

import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.generator.collection.Active
import io.gitlab.arturbosch.detekt.generator.collection.Configuration
import io.gitlab.arturbosch.detekt.generator.collection.DefaultValue
import io.gitlab.arturbosch.detekt.generator.collection.Inactive
import io.gitlab.arturbosch.detekt.generator.collection.Rule
import io.gitlab.arturbosch.detekt.generator.collection.RuleSetProvider
import io.gitlab.arturbosch.detekt.generator.out.yaml
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource

internal class RuleSetConfigPrinterTest {
private val ruleSetProviderTemplate = RuleSetProvider(
name = "rulesetName",
description = "description",
defaultActivationStatus = Inactive
)
private val ruleTemplate = Rule(
name = "ruleName",
description = "",
nonCompliantCodeExample = "",
compliantCodeExample = "",
defaultActivationStatus = Inactive,
severity = "",
debt = "",
aliases = null,
parent = ""
)

private val configurationTemplate = Configuration(
name = "name",
description = "description",
defaultValue = DefaultValue.of(1),
defaultAndroidValue = null,
deprecated = null,
)

@Nested
inner class PrintRuleSet {

@Test
fun `starts with name on top level`() {
val ruleset = ruleSetProviderTemplate.copy(name = "rule-set-name")
val actual = yaml { printRuleSet(ruleset, emptyList()) }
assertThat(actual).startsWith("rule-set-name:\n")
}

@Test
fun `includes all rules`() {
val rules = listOf(ruleTemplate.copy(name = "RuleA"), ruleTemplate.copy(name = "RuleB"))
val actual = yaml { printRuleSet(ruleSetProviderTemplate, rules) }
assertThat(actual.lines()).contains(" RuleA:", " RuleB:")
}

@Nested
inner class ActivationStatus {

@Test
fun `has active property`() {
val ruleset = ruleSetProviderTemplate.copy(defaultActivationStatus = Active("1.0.0"))
val actual = yaml { printRuleSet(ruleset, emptyList()) }
assertThat(actual.lines()).contains(" active: true")
}

@Test
fun `has active property if inactive`() {
val ruleset = ruleSetProviderTemplate.copy(defaultActivationStatus = Inactive)
val actual = yaml { printRuleSet(ruleset, emptyList()) }
assertThat(actual.lines()).contains(" active: false")
}
}
}

@Nested
inner class PrintRule {

@Test
fun `starts with rule name`() {
val rule = ruleTemplate.copy(name = "RuleA")
val actual = yaml { printRule(rule) }
assertThat(actual).startsWith("RuleA:\n")
}

@Nested
inner class ActivationStatus {

@Test
fun `has active property`() {
val rule = ruleTemplate.copy(defaultActivationStatus = Active("1.0.0"))
val actual = yaml { printRule(rule) }
assertThat(actual.lines()).contains(" active: true")
}

@Test
fun `has active property if inactive`() {
val rule = ruleTemplate.copy(defaultActivationStatus = Inactive)
val actual = yaml { printRule(rule) }
assertThat(actual.lines()).contains(" active: false")
}
}

@Nested
inner class AutoCorrect {

@Test
fun `has auto correct property`() {
val rule = ruleTemplate.copy(autoCorrect = true)
val actual = yaml { printRule(rule) }
assertThat(actual.lines()).contains(" autoCorrect: true")
}

@Test
fun `omits auto correct property if false`() {
val rule = ruleTemplate.copy(autoCorrect = false)
val actual = yaml { printRule(rule) }
assertThat(actual).doesNotContain(Config.AUTO_CORRECT_KEY)
}
}

@Nested
inner class Exclusion {

@Test
fun `rule is excluded`() {
val anExclusion = exclusions[0]
val anExcludedRuleName = anExclusion.rules.first()
val rule = ruleTemplate.copy(name = anExcludedRuleName)
val actual = yaml { printRule(rule) }
assertThat(actual.lines()).contains(" excludes: ${anExclusion.pattern}")
}

@Test
fun `omits excludes property if rule is not excluded in any exclusion`() {
val rule = ruleTemplate.copy(name = "ARuleNameThatIsNotExcluded")
val actual = yaml { printRule(rule) }
assertThat(actual).doesNotContain(Config.EXCLUDES_KEY)
}
}
}

@Nested
inner class PrintConfiguration {

@Test
fun `ignore deprecated`() {
val given = configurationTemplate.copy(deprecated = "use something else")
val actual = yaml { printConfiguration(given) }
assertThat(actual).isEmpty()
}

@Nested
inner class DefaultValues {

@Test
fun `int default value`() {
val given = configurationTemplate.copy(defaultValue = DefaultValue.of(99))
val actual = yaml { printConfiguration(given) }
assertThat(actual).isEqualTo("name: 99")
}

@Test
fun `boolean default value`() {
val given = configurationTemplate.copy(defaultValue = DefaultValue.of(false))
val actual = yaml { printConfiguration(given) }
assertThat(actual).isEqualTo("name: false")
}

@ValueSource(
strings = [
"", " ", "a", "a b", "a\$b"
]
)
@ParameterizedTest
fun `string default value is quoted`(value: String) {
val given = configurationTemplate.copy(defaultValue = DefaultValue.of(value))
val actual = yaml { printConfiguration(given) }
assertThat(actual).isEqualTo("name: '$value'")
}

@Test
fun `empty list default value uses array syntax`() {
val given = configurationTemplate.copy(defaultValue = DefaultValue.of(emptyList()))
val actual = yaml { printConfiguration(given) }
assertThat(actual).isEqualTo("name: []")
}

@Test
fun `string list default value`() {
val given = configurationTemplate.copy(defaultValue = DefaultValue.of(listOf("a", "b", "c")))
val actual = yaml { printConfiguration(given) }
val expected = """name:
| - 'a'
| - 'b'
| - 'c'
""".trimMargin()
assertThat(actual).isEqualTo(expected)
}
}
}
}

0 comments on commit 2aad546

Please sign in to comment.