mirror of
https://github.com/jlengrand/detekt.git
synced 2026-03-10 00:01:19 +00:00
Remove Unnecesary @Nested (#4740)
This commit is contained in:
@@ -10,16 +10,37 @@ import java.util.concurrent.atomic.AtomicInteger
|
||||
class ConfigPropertySpec {
|
||||
|
||||
@Nested
|
||||
inner class `Config property delegate` {
|
||||
inner class `simple property` {
|
||||
@Nested
|
||||
inner class `simple property` {
|
||||
inner class `String property` {
|
||||
private val configValue = "value"
|
||||
private val defaultValue = "default"
|
||||
private val subject = object : TestConfigAware("present" to configValue) {
|
||||
val present: String by config(defaultValue)
|
||||
val notPresent: String by config(defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the value provided in config if present`() {
|
||||
assertThat(subject.present).isEqualTo(configValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the default value if not present`() {
|
||||
assertThat(subject.notPresent).isEqualTo(defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Int property` {
|
||||
private val configValue = 99
|
||||
private val defaultValue = -1
|
||||
|
||||
@Nested
|
||||
inner class `String property` {
|
||||
private val configValue = "value"
|
||||
private val defaultValue = "default"
|
||||
inner class `defined as number` {
|
||||
private val subject = object : TestConfigAware("present" to configValue) {
|
||||
val present: String by config(defaultValue)
|
||||
val notPresent: String by config(defaultValue)
|
||||
val present: Int by config(defaultValue)
|
||||
val notPresent: Int by config(defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -34,394 +55,370 @@ class ConfigPropertySpec {
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Int property` {
|
||||
private val configValue = 99
|
||||
private val defaultValue = -1
|
||||
|
||||
@Nested
|
||||
inner class `defined as number` {
|
||||
private val subject = object : TestConfigAware("present" to configValue) {
|
||||
val present: Int by config(defaultValue)
|
||||
val notPresent: Int by config(defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the value provided in config if present`() {
|
||||
assertThat(subject.present).isEqualTo(configValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the default value if not present`() {
|
||||
assertThat(subject.notPresent).isEqualTo(defaultValue)
|
||||
}
|
||||
inner class `defined as string` {
|
||||
private val subject = object : TestConfigAware("present" to "$configValue") {
|
||||
val present: Int by config(defaultValue)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `defined as string` {
|
||||
private val subject = object : TestConfigAware("present" to "$configValue") {
|
||||
val present: Int by config(defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the value provided in config if present`() {
|
||||
assertThat(subject.present).isEqualTo(configValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Boolean property` {
|
||||
private val configValue = false
|
||||
private val defaultValue = true
|
||||
|
||||
@Nested
|
||||
inner class `defined as Boolean` {
|
||||
private val subject = object : TestConfigAware("present" to configValue) {
|
||||
val present: Boolean by config(defaultValue)
|
||||
val notPresent: Boolean by config(defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the value provided in config if present`() {
|
||||
assertThat(subject.present).isEqualTo(configValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the default value if not present`() {
|
||||
assertThat(subject.notPresent).isEqualTo(defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `defined as string` {
|
||||
private val subject = object : TestConfigAware("present" to "$configValue") {
|
||||
val present: Boolean by config(defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the value provided in config if present`() {
|
||||
assertThat(subject.present).isEqualTo(configValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `List property` {
|
||||
private val defaultValue = listOf("x")
|
||||
|
||||
@Nested
|
||||
inner class `defined as list` {
|
||||
private val subject = object : TestConfigAware("present" to "a,b,c") {
|
||||
val present: List<String> by config(defaultValue)
|
||||
val notPresent: List<String> by config(defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the value provided in config if present`() {
|
||||
assertThat(subject.present).isEqualTo(listOf("a", "b", "c"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the default value if not present`() {
|
||||
assertThat(subject.notPresent).isEqualTo(defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `defined as comma separated string` {
|
||||
private val subject = object : TestConfigAware("present" to "a,b,c") {
|
||||
val present: List<String> by config(defaultValue)
|
||||
val notPresent: List<String> by config(defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the value provided in config if present`() {
|
||||
assertThat(subject.present).isEqualTo(listOf("a", "b", "c"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the default value if not present`() {
|
||||
assertThat(subject.notPresent).isEqualTo(defaultValue)
|
||||
}
|
||||
@Test
|
||||
fun `uses the value provided in config if present`() {
|
||||
assertThat(subject.present).isEqualTo(configValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `invalid type` {
|
||||
@Nested
|
||||
inner class `Long property` {
|
||||
private val defaultValue = 1L
|
||||
private val subject = object : TestConfigAware() {
|
||||
val prop: Long by config(defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `throws`() {
|
||||
assertThatThrownBy { subject.prop }
|
||||
.isInstanceOf(IllegalStateException::class.java)
|
||||
.hasMessageContaining("is not supported")
|
||||
}
|
||||
}
|
||||
inner class `Boolean property` {
|
||||
private val configValue = false
|
||||
private val defaultValue = true
|
||||
|
||||
@Nested
|
||||
inner class `Regex property` {
|
||||
private val defaultValue = Regex("a")
|
||||
private val subject = object : TestConfigAware() {
|
||||
val prop: Regex by config(defaultValue)
|
||||
inner class `defined as Boolean` {
|
||||
private val subject = object : TestConfigAware("present" to configValue) {
|
||||
val present: Boolean by config(defaultValue)
|
||||
val notPresent: Boolean by config(defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `throws`() {
|
||||
assertThatThrownBy { subject.prop }
|
||||
.isInstanceOf(IllegalStateException::class.java)
|
||||
.hasMessageContaining("is not supported")
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Set property` {
|
||||
private val defaultValue = setOf("a")
|
||||
private val subject = object : TestConfigAware() {
|
||||
val prop: Set<String> by config(defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `throws`() {
|
||||
assertThatThrownBy { subject.prop }
|
||||
.isInstanceOf(IllegalStateException::class.java)
|
||||
.hasMessageContaining("is not supported")
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `List of Int` {
|
||||
private val defaultValue = listOf(1)
|
||||
private val subject = object : TestConfigAware() {
|
||||
val prop: List<Int> by config(defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `throws`() {
|
||||
assertThatThrownBy { subject.prop }
|
||||
.isInstanceOf(IllegalStateException::class.java)
|
||||
.hasMessageContaining("lists of strings are supported")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `transform` {
|
||||
@Nested
|
||||
inner class `primitive` {
|
||||
@Nested
|
||||
inner class `String property is transformed to regex` {
|
||||
private val defaultValue = ".*"
|
||||
private val configValue = "[a-z]+"
|
||||
private val subject = object : TestConfigAware("present" to configValue) {
|
||||
val present: Regex by config(defaultValue) { it.toRegex() }
|
||||
val notPresent: Regex by config(defaultValue) { it.toRegex() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `applies the mapping function to the configured value`() {
|
||||
assertThat(subject.present.matches("abc")).isTrue
|
||||
assertThat(subject.present.matches("123")).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `applies the mapping function to the default`() {
|
||||
assertThat(subject.notPresent.matches("abc")).isTrue
|
||||
assertThat(subject.notPresent.matches("123")).isTrue
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Int property is transformed to String` {
|
||||
private val configValue = 99
|
||||
private val defaultValue = -1
|
||||
private val subject = object : TestConfigAware("present" to configValue) {
|
||||
val present: String by config(defaultValue) { it.toString() }
|
||||
val notPresent: String by config(defaultValue) { it.toString() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `applies the mapping function to the configured value`() {
|
||||
assertThat(subject.present).isEqualTo("$configValue")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `applies the mapping function to the default`() {
|
||||
assertThat(subject.notPresent).isEqualTo("$defaultValue")
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Boolean property is transformed to String` {
|
||||
private val configValue = true
|
||||
private val defaultValue = false
|
||||
private val subject = object : TestConfigAware("present" to configValue) {
|
||||
val present: String by config(defaultValue) { it.toString() }
|
||||
val notPresent: String by config(defaultValue) { it.toString() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `applies the mapping function to the configured value`() {
|
||||
assertThat(subject.present).isEqualTo("$configValue")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `applies the mapping function to the default`() {
|
||||
assertThat(subject.notPresent).isEqualTo("$defaultValue")
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Boolean property is transformed to String with function reference` {
|
||||
private val defaultValue = false
|
||||
private val subject = object : TestConfigAware() {
|
||||
val prop1: String by config(defaultValue, Boolean::toString)
|
||||
val prop2: String by config(transformer = Boolean::toString, defaultValue = defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `transforms properties`() {
|
||||
assertThat(subject.prop1).isEqualTo("$defaultValue")
|
||||
assertThat(subject.prop2).isEqualTo("$defaultValue")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `list of strings` {
|
||||
private val defaultValue = listOf("99")
|
||||
private val subject = object : TestConfigAware("present" to "1,2,3") {
|
||||
val present: Int by config(defaultValue) { it.sumOf(String::toInt) }
|
||||
val notPresent: Int by config(defaultValue) { it.sumOf(String::toInt) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `applies transformer to list configured`() {
|
||||
assertThat(subject.present).isEqualTo(6)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `applies transformer to default list`() {
|
||||
assertThat(subject.notPresent).isEqualTo(99)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `empty list of strings` {
|
||||
private val subject = object : TestConfigAware() {
|
||||
val defaultValue: List<String> = emptyList()
|
||||
val prop1: List<Int> by config(defaultValue) { it.map(String::toInt) }
|
||||
val prop2: List<Int> by config(emptyList<String>()) { it.map(String::toInt) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `can be defined as variable`() {
|
||||
assertThat(subject.prop1).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `can be defined using listOf`() {
|
||||
assertThat(subject.prop2).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `memoization` {
|
||||
private val subject = object : TestConfigAware() {
|
||||
val counter = AtomicInteger(0)
|
||||
val prop: String by config(1) {
|
||||
counter.getAndIncrement()
|
||||
it.toString()
|
||||
}
|
||||
|
||||
fun useProperty(): String {
|
||||
return "something with $prop"
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `transformer is called only once`() {
|
||||
repeat(5) {
|
||||
assertThat(subject.useProperty()).isEqualTo("something with 1")
|
||||
}
|
||||
assertThat(subject.counter.get()).isEqualTo(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
@Nested
|
||||
inner class `configWithFallback` {
|
||||
@Nested
|
||||
inner class `primitive` {
|
||||
private val configValue = 99
|
||||
private val defaultValue = 0
|
||||
private val fallbackValue = -1
|
||||
private val subject =
|
||||
object : TestConfigAware("present" to "$configValue", "fallback" to fallbackValue) {
|
||||
private val fallback: Int by config(42)
|
||||
private val missing: Int by config(42)
|
||||
val present: Int by configWithFallback(::fallback, defaultValue)
|
||||
val notPresentWithFallback: Int by configWithFallback(::fallback, defaultValue)
|
||||
val notPresentFallbackMissing: Int by configWithFallback(::missing, defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the value provided in config if present`() {
|
||||
assertThat(subject.present).isEqualTo(configValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the value from fallback property if value is missing and fallback exists`() {
|
||||
assertThat(subject.notPresentWithFallback).isEqualTo(fallbackValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the default value if not present`() {
|
||||
assertThat(subject.notPresentFallbackMissing).isEqualTo(defaultValue)
|
||||
assertThat(subject.notPresent).isEqualTo(defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `with transformation` {
|
||||
private val configValue = 99
|
||||
private val defaultValue = 0
|
||||
private val fallbackValue = -1
|
||||
private val fallbackOffset = 10
|
||||
private val subject = object : TestConfigAware("present" to configValue, "fallback" to fallbackValue) {
|
||||
private val fallback: String by config(42) { (it + fallbackOffset).toString() }
|
||||
private val missing: String by config(42) { (it + fallbackOffset).toString() }
|
||||
val present: String by configWithFallback(::fallback, defaultValue) { v ->
|
||||
v.toString()
|
||||
}
|
||||
val notPresentWithFallback: String by configWithFallback(::fallback, defaultValue) { v ->
|
||||
v.toString()
|
||||
}
|
||||
val notPresentFallbackMissing: String by configWithFallback(::missing, defaultValue) { v ->
|
||||
v.toString()
|
||||
}
|
||||
inner class `defined as string` {
|
||||
private val subject = object : TestConfigAware("present" to "$configValue") {
|
||||
val present: Boolean by config(defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the value provided in config if present`() {
|
||||
assertThat(subject.present).isEqualTo("$configValue")
|
||||
assertThat(subject.present).isEqualTo(configValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `List property` {
|
||||
private val defaultValue = listOf("x")
|
||||
|
||||
@Nested
|
||||
inner class `defined as list` {
|
||||
private val subject = object : TestConfigAware("present" to "a,b,c") {
|
||||
val present: List<String> by config(defaultValue)
|
||||
val notPresent: List<String> by config(defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `transforms the value from fallback property if value is missing and fallback exists`() {
|
||||
assertThat(subject.notPresentWithFallback).isEqualTo("${fallbackValue + fallbackOffset}")
|
||||
fun `uses the value provided in config if present`() {
|
||||
assertThat(subject.present).isEqualTo(listOf("a", "b", "c"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the default value if not present`() {
|
||||
assertThat(subject.notPresentFallbackMissing).isEqualTo("$defaultValue")
|
||||
assertThat(subject.notPresent).isEqualTo(defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `defined as comma separated string` {
|
||||
private val subject = object : TestConfigAware("present" to "a,b,c") {
|
||||
val present: List<String> by config(defaultValue)
|
||||
val notPresent: List<String> by config(defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the value provided in config if present`() {
|
||||
assertThat(subject.present).isEqualTo(listOf("a", "b", "c"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the default value if not present`() {
|
||||
assertThat(subject.notPresent).isEqualTo(defaultValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `invalid type` {
|
||||
@Nested
|
||||
inner class `Long property` {
|
||||
private val defaultValue = 1L
|
||||
private val subject = object : TestConfigAware() {
|
||||
val prop: Long by config(defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `throws`() {
|
||||
assertThatThrownBy { subject.prop }
|
||||
.isInstanceOf(IllegalStateException::class.java)
|
||||
.hasMessageContaining("is not supported")
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Regex property` {
|
||||
private val defaultValue = Regex("a")
|
||||
private val subject = object : TestConfigAware() {
|
||||
val prop: Regex by config(defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `throws`() {
|
||||
assertThatThrownBy { subject.prop }
|
||||
.isInstanceOf(IllegalStateException::class.java)
|
||||
.hasMessageContaining("is not supported")
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Set property` {
|
||||
private val defaultValue = setOf("a")
|
||||
private val subject = object : TestConfigAware() {
|
||||
val prop: Set<String> by config(defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `throws`() {
|
||||
assertThatThrownBy { subject.prop }
|
||||
.isInstanceOf(IllegalStateException::class.java)
|
||||
.hasMessageContaining("is not supported")
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `List of Int` {
|
||||
private val defaultValue = listOf(1)
|
||||
private val subject = object : TestConfigAware() {
|
||||
val prop: List<Int> by config(defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `throws`() {
|
||||
assertThatThrownBy { subject.prop }
|
||||
.isInstanceOf(IllegalStateException::class.java)
|
||||
.hasMessageContaining("lists of strings are supported")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `transform` {
|
||||
@Nested
|
||||
inner class `primitive` {
|
||||
@Nested
|
||||
inner class `String property is transformed to regex` {
|
||||
private val defaultValue = ".*"
|
||||
private val configValue = "[a-z]+"
|
||||
private val subject = object : TestConfigAware("present" to configValue) {
|
||||
val present: Regex by config(defaultValue) { it.toRegex() }
|
||||
val notPresent: Regex by config(defaultValue) { it.toRegex() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `applies the mapping function to the configured value`() {
|
||||
assertThat(subject.present.matches("abc")).isTrue
|
||||
assertThat(subject.present.matches("123")).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `applies the mapping function to the default`() {
|
||||
assertThat(subject.notPresent.matches("abc")).isTrue
|
||||
assertThat(subject.notPresent.matches("123")).isTrue
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Int property is transformed to String` {
|
||||
private val configValue = 99
|
||||
private val defaultValue = -1
|
||||
private val subject = object : TestConfigAware("present" to configValue) {
|
||||
val present: String by config(defaultValue) { it.toString() }
|
||||
val notPresent: String by config(defaultValue) { it.toString() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `applies the mapping function to the configured value`() {
|
||||
assertThat(subject.present).isEqualTo("$configValue")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `applies the mapping function to the default`() {
|
||||
assertThat(subject.notPresent).isEqualTo("$defaultValue")
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Boolean property is transformed to String` {
|
||||
private val configValue = true
|
||||
private val defaultValue = false
|
||||
private val subject = object : TestConfigAware("present" to configValue) {
|
||||
val present: String by config(defaultValue) { it.toString() }
|
||||
val notPresent: String by config(defaultValue) { it.toString() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `applies the mapping function to the configured value`() {
|
||||
assertThat(subject.present).isEqualTo("$configValue")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `applies the mapping function to the default`() {
|
||||
assertThat(subject.notPresent).isEqualTo("$defaultValue")
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Boolean property is transformed to String with function reference` {
|
||||
private val defaultValue = false
|
||||
private val subject = object : TestConfigAware() {
|
||||
val prop1: String by config(defaultValue, Boolean::toString)
|
||||
val prop2: String by config(transformer = Boolean::toString, defaultValue = defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `transforms properties`() {
|
||||
assertThat(subject.prop1).isEqualTo("$defaultValue")
|
||||
assertThat(subject.prop2).isEqualTo("$defaultValue")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `list of strings` {
|
||||
private val defaultValue = listOf("99")
|
||||
private val subject = object : TestConfigAware("present" to "1,2,3") {
|
||||
val present: Int by config(defaultValue) { it.sumOf(String::toInt) }
|
||||
val notPresent: Int by config(defaultValue) { it.sumOf(String::toInt) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `applies transformer to list configured`() {
|
||||
assertThat(subject.present).isEqualTo(6)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `applies transformer to default list`() {
|
||||
assertThat(subject.notPresent).isEqualTo(99)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `empty list of strings` {
|
||||
private val subject = object : TestConfigAware() {
|
||||
val defaultValue: List<String> = emptyList()
|
||||
val prop1: List<Int> by config(defaultValue) { it.map(String::toInt) }
|
||||
val prop2: List<Int> by config(emptyList<String>()) { it.map(String::toInt) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `can be defined as variable`() {
|
||||
assertThat(subject.prop1).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `can be defined using listOf`() {
|
||||
assertThat(subject.prop2).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `memoization` {
|
||||
private val subject = object : TestConfigAware() {
|
||||
val counter = AtomicInteger(0)
|
||||
val prop: String by config(1) {
|
||||
counter.getAndIncrement()
|
||||
it.toString()
|
||||
}
|
||||
|
||||
fun useProperty(): String {
|
||||
return "something with $prop"
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `transformer is called only once`() {
|
||||
repeat(5) {
|
||||
assertThat(subject.useProperty()).isEqualTo("something with 1")
|
||||
}
|
||||
assertThat(subject.counter.get()).isEqualTo(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
@Nested
|
||||
inner class `configWithFallback` {
|
||||
@Nested
|
||||
inner class `primitive` {
|
||||
private val configValue = 99
|
||||
private val defaultValue = 0
|
||||
private val fallbackValue = -1
|
||||
private val subject =
|
||||
object : TestConfigAware("present" to "$configValue", "fallback" to fallbackValue) {
|
||||
private val fallback: Int by config(42)
|
||||
private val missing: Int by config(42)
|
||||
val present: Int by configWithFallback(::fallback, defaultValue)
|
||||
val notPresentWithFallback: Int by configWithFallback(::fallback, defaultValue)
|
||||
val notPresentFallbackMissing: Int by configWithFallback(::missing, defaultValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the value provided in config if present`() {
|
||||
assertThat(subject.present).isEqualTo(configValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the value from fallback property if value is missing and fallback exists`() {
|
||||
assertThat(subject.notPresentWithFallback).isEqualTo(fallbackValue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the default value if not present`() {
|
||||
assertThat(subject.notPresentFallbackMissing).isEqualTo(defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `with transformation` {
|
||||
private val configValue = 99
|
||||
private val defaultValue = 0
|
||||
private val fallbackValue = -1
|
||||
private val fallbackOffset = 10
|
||||
private val subject = object : TestConfigAware("present" to configValue, "fallback" to fallbackValue) {
|
||||
private val fallback: String by config(42) { (it + fallbackOffset).toString() }
|
||||
private val missing: String by config(42) { (it + fallbackOffset).toString() }
|
||||
val present: String by configWithFallback(::fallback, defaultValue) { v ->
|
||||
v.toString()
|
||||
}
|
||||
val notPresentWithFallback: String by configWithFallback(::fallback, defaultValue) { v ->
|
||||
v.toString()
|
||||
}
|
||||
val notPresentFallbackMissing: String by configWithFallback(::missing, defaultValue) { v ->
|
||||
v.toString()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the value provided in config if present`() {
|
||||
assertThat(subject.present).isEqualTo("$configValue")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `transforms the value from fallback property if value is missing and fallback exists`() {
|
||||
assertThat(subject.notPresentWithFallback).isEqualTo("${fallbackValue + fallbackOffset}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the default value if not present`() {
|
||||
assertThat(subject.notPresentFallbackMissing).isEqualTo("$defaultValue")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,84 +12,81 @@ import java.nio.file.Paths
|
||||
|
||||
class EntitySpec {
|
||||
|
||||
private val path = Paths.get("/full/path/to/Test.kt")
|
||||
private val code = compileContentForTest(
|
||||
"""
|
||||
package test
|
||||
|
||||
class C : Any() {
|
||||
|
||||
private fun memberFun(): Int = 5
|
||||
}
|
||||
|
||||
fun topLevelFun(number: Int) = Unit
|
||||
""".trimIndent(),
|
||||
path.toString()
|
||||
)
|
||||
|
||||
@Nested
|
||||
inner class `entity signatures` {
|
||||
private val path = Paths.get("/full/path/to/Test.kt")
|
||||
private val code = compileContentForTest(
|
||||
"""
|
||||
package test
|
||||
inner class `Named functions` {
|
||||
|
||||
class C : Any() {
|
||||
private val functions = code.collectDescendantsOfType<KtNamedFunction>()
|
||||
|
||||
private fun memberFun(): Int = 5
|
||||
}
|
||||
@Test
|
||||
fun `includes full function header, class name and filename`() {
|
||||
val memberFunction = functions.first { it.name == "memberFun" }
|
||||
|
||||
fun topLevelFun(number: Int) = Unit
|
||||
""".trimIndent(),
|
||||
path.toString()
|
||||
)
|
||||
|
||||
@Nested
|
||||
inner class `Named functions` {
|
||||
|
||||
private val functions = code.collectDescendantsOfType<KtNamedFunction>()
|
||||
|
||||
@Test
|
||||
fun `includes full function header, class name and filename`() {
|
||||
val memberFunction = functions.first { it.name == "memberFun" }
|
||||
|
||||
assertThat(Entity.atName(memberFunction).signature)
|
||||
.isEqualTo("Test.kt\$C\$private fun memberFun(): Int")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `includes full function header and filename for a top level function`() {
|
||||
val topLevelFunction = functions.first { it.name == "topLevelFun" }
|
||||
|
||||
assertThat(Entity.atName(topLevelFunction).signature)
|
||||
.isEqualTo("Test.kt\$fun topLevelFun(number: Int)")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `includes function name in entity compact`() {
|
||||
val memberFunction = functions.first { it.name == "memberFun" }
|
||||
|
||||
assertThat(Entity.atName(memberFunction).compact())
|
||||
.isEqualTo("[memberFun] at $path:5:17")
|
||||
}
|
||||
assertThat(Entity.atName(memberFunction).signature)
|
||||
.isEqualTo("Test.kt\$C\$private fun memberFun(): Int")
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Classes` {
|
||||
private val clazz = requireNotNull(code.findDescendantOfType<KtClass>())
|
||||
@Test
|
||||
fun `includes full function header and filename for a top level function`() {
|
||||
val topLevelFunction = functions.first { it.name == "topLevelFun" }
|
||||
|
||||
@Test
|
||||
fun `includes full class signature`() {
|
||||
assertThat(Entity.atName(clazz).signature).isEqualTo("Test.kt\$C : Any")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `includes class name in entity compact`() {
|
||||
assertThat(Entity.atName(clazz).compact()).isEqualTo("[C] at $path:3:7")
|
||||
}
|
||||
assertThat(Entity.atName(topLevelFunction).signature)
|
||||
.isEqualTo("Test.kt\$fun topLevelFun(number: Int)")
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Files` {
|
||||
@Test
|
||||
fun `includes function name in entity compact`() {
|
||||
val memberFunction = functions.first { it.name == "memberFun" }
|
||||
|
||||
@Test
|
||||
fun `includes package and file name in entity signature`() {
|
||||
assertThat(Entity.from(code).signature).isEqualTo("Test.kt\$test.Test.kt")
|
||||
assertThat(Entity.atPackageOrFirstDecl(code).signature).isEqualTo("Test.kt\$test.Test.kt")
|
||||
}
|
||||
assertThat(Entity.atName(memberFunction).compact())
|
||||
.isEqualTo("[memberFun] at $path:5:17")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `includes file name in entity compact`() {
|
||||
val expectedResult = "[Test.kt] at $path:1:1"
|
||||
@Nested
|
||||
inner class `Classes` {
|
||||
private val clazz = requireNotNull(code.findDescendantOfType<KtClass>())
|
||||
|
||||
assertThat(Entity.from(code).compact()).isEqualTo(expectedResult)
|
||||
assertThat(Entity.atPackageOrFirstDecl(code).compact()).isEqualTo(expectedResult)
|
||||
}
|
||||
@Test
|
||||
fun `includes full class signature`() {
|
||||
assertThat(Entity.atName(clazz).signature).isEqualTo("Test.kt\$C : Any")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `includes class name in entity compact`() {
|
||||
assertThat(Entity.atName(clazz).compact()).isEqualTo("[C] at $path:3:7")
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Files` {
|
||||
|
||||
@Test
|
||||
fun `includes package and file name in entity signature`() {
|
||||
assertThat(Entity.from(code).signature).isEqualTo("Test.kt\$test.Test.kt")
|
||||
assertThat(Entity.atPackageOrFirstDecl(code).signature).isEqualTo("Test.kt\$test.Test.kt")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `includes file name in entity compact`() {
|
||||
val expectedResult = "[Test.kt] at $path:1:1"
|
||||
|
||||
assertThat(Entity.from(code).compact()).isEqualTo(expectedResult)
|
||||
assertThat(Entity.atPackageOrFirstDecl(code).compact()).isEqualTo(expectedResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,26 +2,21 @@ package io.gitlab.arturbosch.detekt.api
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatIllegalStateException
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class MetricSpec {
|
||||
|
||||
@Nested
|
||||
inner class `Metrics` {
|
||||
@Test
|
||||
fun `should convert double values to int`() {
|
||||
val metric = Metric("LOC", 0.33, 0.10, 100)
|
||||
assertThat(metric.doubleValue()).isEqualTo(0.33)
|
||||
assertThat(metric.doubleThreshold()).isEqualTo(0.10)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should convert double values to int`() {
|
||||
val metric = Metric("LOC", 0.33, 0.10, 100)
|
||||
assertThat(metric.doubleValue()).isEqualTo(0.33)
|
||||
assertThat(metric.doubleThreshold()).isEqualTo(0.10)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should throw error if double value is asked for int metric`() {
|
||||
assertThatIllegalStateException().isThrownBy {
|
||||
Metric("LOC", 100, 50).doubleValue()
|
||||
}
|
||||
@Test
|
||||
fun `should throw error if double value is asked for int metric`() {
|
||||
assertThatIllegalStateException().isThrownBy {
|
||||
Metric("LOC", 100, 50).doubleValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,54 +2,49 @@ package io.gitlab.arturbosch.detekt.api
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatCode
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.random.Random
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
class PropertiesAwareSpec {
|
||||
|
||||
@Nested
|
||||
inner class `Implementations can store and retrieve properties` {
|
||||
|
||||
private val hash = Random(1).nextInt()
|
||||
private val store = object : PropertiesAware {
|
||||
override val properties: MutableMap<String, Any> = HashMap()
|
||||
override fun register(key: String, value: Any) {
|
||||
properties[key] = value
|
||||
private val hash = Random(1).nextInt()
|
||||
private val store = object : PropertiesAware {
|
||||
override val properties: MutableMap<String, Any> = HashMap()
|
||||
override fun register(key: String, value: Any) {
|
||||
properties[key] = value
|
||||
}
|
||||
}.apply {
|
||||
register("bool", true)
|
||||
register("string", "test")
|
||||
register("number", 5)
|
||||
register("set", setOf(1, 2, 3))
|
||||
register(
|
||||
"any",
|
||||
object : Any() {
|
||||
override fun equals(other: Any?): Boolean = hashCode() == other.hashCode()
|
||||
override fun hashCode(): Int = hash
|
||||
}
|
||||
}.apply {
|
||||
register("bool", true)
|
||||
register("string", "test")
|
||||
register("number", 5)
|
||||
register("set", setOf(1, 2, 3))
|
||||
register(
|
||||
"any",
|
||||
object : Any() {
|
||||
override fun equals(other: Any?): Boolean = hashCode() == other.hashCode()
|
||||
override fun hashCode(): Int = hash
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `can retrieve the actual typed values`() {
|
||||
assertThat(store.getOrNull<Boolean>("bool")).isEqualTo(true)
|
||||
assertThat(store.getOrNull<String>("string")).isEqualTo("test")
|
||||
assertThat(store.getOrNull<Int>("number")).isEqualTo(5)
|
||||
assertThat(store.getOrNull<Set<Int>>("set")).isEqualTo(setOf(1, 2, 3))
|
||||
assertThat(store.getOrNull<Any>("any").hashCode()).isEqualTo(hash)
|
||||
}
|
||||
@Test
|
||||
fun `can retrieve the actual typed values`() {
|
||||
assertThat(store.getOrNull<Boolean>("bool")).isEqualTo(true)
|
||||
assertThat(store.getOrNull<String>("string")).isEqualTo("test")
|
||||
assertThat(store.getOrNull<Int>("number")).isEqualTo(5)
|
||||
assertThat(store.getOrNull<Set<Int>>("set")).isEqualTo(setOf(1, 2, 3))
|
||||
assertThat(store.getOrNull<Any>("any").hashCode()).isEqualTo(hash)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns null on absent values`() {
|
||||
assertThat(store.getOrNull<Boolean>("absent")).isEqualTo(null)
|
||||
}
|
||||
@Test
|
||||
fun `returns null on absent values`() {
|
||||
assertThat(store.getOrNull<Boolean>("absent")).isEqualTo(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `throws an error on wrong type`() {
|
||||
assertThatCode { store.getOrNull<Double>("bool") }
|
||||
.isInstanceOf(IllegalStateException::class.java)
|
||||
}
|
||||
@Test
|
||||
fun `throws an error on wrong type`() {
|
||||
assertThatCode { store.getOrNull<Double>("bool") }
|
||||
.isInstanceOf(IllegalStateException::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,48 +11,45 @@ import org.junit.jupiter.api.Test
|
||||
class RuleSetConfigPropertySpec {
|
||||
|
||||
@Nested
|
||||
inner class `Rule set config property delegate` {
|
||||
@Nested
|
||||
inner class `boolean property` {
|
||||
@Test
|
||||
fun `reads the value in config if present`() {
|
||||
assertThat(TestRuleSetProvider.android.value(TestConfig("android" to "false")))
|
||||
.isEqualTo(false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the default value in config if not present`() {
|
||||
assertThat(TestRuleSetProvider.android.value(TestConfig()))
|
||||
.isEqualTo(true)
|
||||
}
|
||||
inner class `boolean property` {
|
||||
@Test
|
||||
fun `reads the value in config if present`() {
|
||||
assertThat(TestRuleSetProvider.android.value(TestConfig("android" to "false")))
|
||||
.isEqualTo(false)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `int property` {
|
||||
@Test
|
||||
fun `reads the value in config if present`() {
|
||||
assertThat(TestRuleSetProvider.number.value(TestConfig("number" to "37"))).isEqualTo(37)
|
||||
}
|
||||
@Test
|
||||
fun `uses the default value in config if not present`() {
|
||||
assertThat(TestRuleSetProvider.android.value(TestConfig()))
|
||||
.isEqualTo(true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the default value in config if not present`() {
|
||||
assertThat(TestRuleSetProvider.number.value(TestConfig())).isEqualTo(42)
|
||||
}
|
||||
@Nested
|
||||
inner class `int property` {
|
||||
@Test
|
||||
fun `reads the value in config if present`() {
|
||||
assertThat(TestRuleSetProvider.number.value(TestConfig("number" to "37"))).isEqualTo(37)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `string property` {
|
||||
@Test
|
||||
fun `reads the value in config if present`() {
|
||||
assertThat(TestRuleSetProvider.fileName.value(TestConfig("fileName" to "main.kt")))
|
||||
.isEqualTo("main.kt")
|
||||
}
|
||||
@Test
|
||||
fun `uses the default value in config if not present`() {
|
||||
assertThat(TestRuleSetProvider.number.value(TestConfig())).isEqualTo(42)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the default value in config if not present`() {
|
||||
assertThat(TestRuleSetProvider.fileName.value(TestConfig()))
|
||||
.isEqualTo("test.kt")
|
||||
}
|
||||
@Nested
|
||||
inner class `string property` {
|
||||
@Test
|
||||
fun `reads the value in config if present`() {
|
||||
assertThat(TestRuleSetProvider.fileName.value(TestConfig("fileName" to "main.kt")))
|
||||
.isEqualTo("main.kt")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses the default value in config if not present`() {
|
||||
assertThat(TestRuleSetProvider.fileName.value(TestConfig()))
|
||||
.isEqualTo("test.kt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,274 +7,271 @@ import org.junit.jupiter.api.Test
|
||||
|
||||
class SimpleGlobSpec {
|
||||
@Nested
|
||||
inner class `glob` {
|
||||
@Nested
|
||||
inner class `empty pattern` {
|
||||
private val subject = SimpleGlob.of("")
|
||||
inner class `empty pattern` {
|
||||
private val subject = SimpleGlob.of("")
|
||||
|
||||
@Test
|
||||
fun `matches an empty string`() {
|
||||
val actual = subject.matches("")
|
||||
assertThat(actual).isTrue()
|
||||
@Test
|
||||
fun `matches an empty string`() {
|
||||
val actual = subject.matches("")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match a blank string`() {
|
||||
val actual = subject.matches(" ")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `blank pattern` {
|
||||
private val subject = SimpleGlob.of(" \t")
|
||||
|
||||
@Test
|
||||
fun `matches an empty string`() {
|
||||
val actual = subject.matches(" \t")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match a different string`() {
|
||||
val actual = subject.matches(" ")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Static pattern` {
|
||||
private val subject = SimpleGlob.of("abc")
|
||||
|
||||
@Test
|
||||
fun `matches the same string`() {
|
||||
val actual = subject.matches("abc")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match a other string`() {
|
||||
val actual = subject.matches("aaa")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Asterisk wildcard` {
|
||||
@Nested
|
||||
inner class `single wildcard` {
|
||||
@Nested
|
||||
inner class `pattern with wildcard at the beginning` {
|
||||
private val subject = SimpleGlob.of("*xyz")
|
||||
|
||||
@Test
|
||||
fun `matches pattern exactly`() {
|
||||
val actual = subject.matches("xyz")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `matches pattern with anything before`() {
|
||||
val actual = subject.matches("abcxyz")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match with anything after`() {
|
||||
val actual = subject.matches("xyzabc")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match a blank string`() {
|
||||
val actual = subject.matches(" ")
|
||||
assertThat(actual).isFalse()
|
||||
@Nested
|
||||
inner class `Pattern with wildcard at the end` {
|
||||
private val subject = SimpleGlob.of("xyz*")
|
||||
|
||||
@Test
|
||||
fun `matches pattern exactly`() {
|
||||
val actual = subject.matches("xyz")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `matches pattern with anything after`() {
|
||||
val actual = subject.matches("xyzabc")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match with anything before`() {
|
||||
val actual = subject.matches("abcxyz")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Pattern with wildcard at the middle` {
|
||||
private val subject = SimpleGlob.of("x*yz")
|
||||
|
||||
@Test
|
||||
fun `matches pattern exactly`() {
|
||||
val actual = subject.matches("xyz")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `matches pattern with anything in between`() {
|
||||
val actual = subject.matches("xaaaayz")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match with anything before`() {
|
||||
val actual = subject.matches("axyz")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match with anything after`() {
|
||||
val actual = subject.matches("xyza")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `blank pattern` {
|
||||
private val subject = SimpleGlob.of(" \t")
|
||||
inner class `multiple wildcards` {
|
||||
private val subject = SimpleGlob.of("x*yz*")
|
||||
|
||||
@Test
|
||||
fun `matches an empty string`() {
|
||||
val actual = subject.matches(" \t")
|
||||
fun `matches pattern`() {
|
||||
val actual = subject.matches("x.aaa.yz.bbb")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match a different string`() {
|
||||
val actual = subject.matches(" ")
|
||||
assertThat(actual).isFalse()
|
||||
@Nested
|
||||
inner class `Questionmark wildcard` {
|
||||
@Nested
|
||||
inner class `single wildcard` {
|
||||
@Nested
|
||||
inner class `pattern with wildcard at the beginning` {
|
||||
private val subject = SimpleGlob.of("?xyz")
|
||||
|
||||
@Test
|
||||
fun `matches with any character before`() {
|
||||
val actual = subject.matches("_xyz")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match with anything before`() {
|
||||
val actual = subject.matches("xyz")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match with more than on character before`() {
|
||||
val actual = subject.matches("aaxyz")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `pattern with wildcard at the end` {
|
||||
private val subject = SimpleGlob.of("xyz?")
|
||||
|
||||
@Test
|
||||
fun `matches with any character after`() {
|
||||
val actual = subject.matches("xyz_")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match with anything after`() {
|
||||
val actual = subject.matches("xyz")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match with more than on character after`() {
|
||||
val actual = subject.matches("xyz_a")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `pattern with wildcard at the middle` {
|
||||
private val subject = SimpleGlob.of("x?yz")
|
||||
|
||||
@Test
|
||||
fun `matches with any single character`() {
|
||||
val actual = subject.matches("x_yz")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match with more than one character`() {
|
||||
val actual = subject.matches("x_a_yz")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Static pattern` {
|
||||
private val subject = SimpleGlob.of("abc")
|
||||
inner class `multiple wildcards` {
|
||||
private val subject = SimpleGlob.of("x?y?z")
|
||||
|
||||
@Test
|
||||
fun `matches pattern`() {
|
||||
val actual = subject.matches("x.y.z")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `characters that have a special meaning in regular expression must be escaped` {
|
||||
@Nested
|
||||
inner class `Period` {
|
||||
private val subject = SimpleGlob.of("a.b.c")
|
||||
|
||||
@Test
|
||||
fun `matches the same string`() {
|
||||
val actual = subject.matches("abc")
|
||||
val actual = subject.matches("a.b.c")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match a other string`() {
|
||||
val actual = subject.matches("aaa")
|
||||
val actual = subject.matches("a_b_c")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Asterisk wildcard` {
|
||||
@Nested
|
||||
inner class `single wildcard` {
|
||||
@Nested
|
||||
inner class `pattern with wildcard at the beginning` {
|
||||
private val subject = SimpleGlob.of("*xyz")
|
||||
inner class `Backslash` {
|
||||
private val subject = SimpleGlob.of("""ab\d""")
|
||||
|
||||
@Test
|
||||
fun `matches pattern exactly`() {
|
||||
val actual = subject.matches("xyz")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `matches pattern with anything before`() {
|
||||
val actual = subject.matches("abcxyz")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match with anything after`() {
|
||||
val actual = subject.matches("xyzabc")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Pattern with wildcard at the end` {
|
||||
private val subject = SimpleGlob.of("xyz*")
|
||||
|
||||
@Test
|
||||
fun `matches pattern exactly`() {
|
||||
val actual = subject.matches("xyz")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `matches pattern with anything after`() {
|
||||
val actual = subject.matches("xyzabc")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match with anything before`() {
|
||||
val actual = subject.matches("abcxyz")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Pattern with wildcard at the middle` {
|
||||
private val subject = SimpleGlob.of("x*yz")
|
||||
|
||||
@Test
|
||||
fun `matches pattern exactly`() {
|
||||
val actual = subject.matches("xyz")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `matches pattern with anything in between`() {
|
||||
val actual = subject.matches("xaaaayz")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match with anything before`() {
|
||||
val actual = subject.matches("axyz")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match with anything after`() {
|
||||
val actual = subject.matches("xyza")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `multiple wildcards` {
|
||||
private val subject = SimpleGlob.of("x*yz*")
|
||||
|
||||
@Test
|
||||
fun `matches pattern`() {
|
||||
val actual = subject.matches("x.aaa.yz.bbb")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Questionmark wildcard` {
|
||||
@Nested
|
||||
inner class `single wildcard` {
|
||||
@Nested
|
||||
inner class `pattern with wildcard at the beginning` {
|
||||
private val subject = SimpleGlob.of("?xyz")
|
||||
|
||||
@Test
|
||||
fun `matches with any character before`() {
|
||||
val actual = subject.matches("_xyz")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match with anything before`() {
|
||||
val actual = subject.matches("xyz")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match with more than on character before`() {
|
||||
val actual = subject.matches("aaxyz")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `pattern with wildcard at the end` {
|
||||
private val subject = SimpleGlob.of("xyz?")
|
||||
|
||||
@Test
|
||||
fun `matches with any character after`() {
|
||||
val actual = subject.matches("xyz_")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match with anything after`() {
|
||||
val actual = subject.matches("xyz")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match with more than on character after`() {
|
||||
val actual = subject.matches("xyz_a")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `pattern with wildcard at the middle` {
|
||||
private val subject = SimpleGlob.of("x?yz")
|
||||
|
||||
@Test
|
||||
fun `matches with any single character`() {
|
||||
val actual = subject.matches("x_yz")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match with more than one character`() {
|
||||
val actual = subject.matches("x_a_yz")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `multiple wildcards` {
|
||||
private val subject = SimpleGlob.of("x?y?z")
|
||||
|
||||
@Test
|
||||
fun `matches pattern`() {
|
||||
val actual = subject.matches("x.y.z")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `characters that have a special meaning in regular expression must be escaped` {
|
||||
@Nested
|
||||
inner class `Period` {
|
||||
private val subject = SimpleGlob.of("a.b.c")
|
||||
|
||||
@Test
|
||||
fun `matches the same string`() {
|
||||
val actual = subject.matches("a.b.c")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match a other string`() {
|
||||
val actual = subject.matches("a_b_c")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Backslash` {
|
||||
private val subject = SimpleGlob.of("""ab\d""")
|
||||
|
||||
@Test
|
||||
fun `matches the same string`() {
|
||||
val actual = subject.matches("""ab\d""")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match a other string`() {
|
||||
val actual = subject.matches("ab5")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `invalid pattern` {
|
||||
@Test
|
||||
fun `fails during creation`() {
|
||||
assertThatThrownBy { SimpleGlob.of("""a[b""") }
|
||||
.isInstanceOf(IllegalArgumentException::class.java)
|
||||
fun `matches the same string`() {
|
||||
val actual = subject.matches("""ab\d""")
|
||||
assertThat(actual).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not match a other string`() {
|
||||
val actual = subject.matches("ab5")
|
||||
assertThat(actual).isFalse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `invalid pattern` {
|
||||
@Test
|
||||
fun `fails during creation`() {
|
||||
assertThatThrownBy { SimpleGlob.of("""a[b""") }
|
||||
.isInstanceOf(IllegalArgumentException::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ class MainSpec {
|
||||
@Nested
|
||||
inner class `Build runner` {
|
||||
|
||||
fun runnerConfigs(): List<Arguments> {
|
||||
@Suppress("UnusedPrivateMember")
|
||||
private fun runnerConfigs(): List<Arguments> {
|
||||
return listOf(
|
||||
arguments(arrayOf("--generate-config"), ConfigExporter::class),
|
||||
arguments(arrayOf("--run-rule", "RuleSet:Rule"), Runner::class),
|
||||
|
||||
@@ -9,101 +9,97 @@ import java.nio.file.Paths
|
||||
|
||||
class PathFiltersSpec {
|
||||
|
||||
@Test
|
||||
fun `should load single filter`() {
|
||||
val filters = CliArgs { excludes = "**/one/**" }.toSpecFilters()
|
||||
assertThat(filters?.isIgnored(Paths.get("/one/path"))).isTrue()
|
||||
assertThat(filters?.isIgnored(Paths.get("/two/path"))).isFalse()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `parse different filter settings` {
|
||||
|
||||
inner class `parsing with different nullability combinations of path filters` {
|
||||
@Test
|
||||
fun `should load single filter`() {
|
||||
val filters = CliArgs { excludes = "**/one/**" }.toSpecFilters()
|
||||
assertThat(filters?.isIgnored(Paths.get("/one/path"))).isTrue()
|
||||
assertThat(filters?.isIgnored(Paths.get("/two/path"))).isFalse()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `parsing with different nullability combinations of path filters` {
|
||||
@Test
|
||||
fun `returns an empty path filter when includes are empty and excludes are empty`() {
|
||||
val pathFilter = PathFilters.of(emptyList(), emptyList())
|
||||
assertThat(pathFilter).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parses includes correctly`() {
|
||||
val pathFilter = PathFilters.of(listOf("**/one/**", "**/two/**"), emptyList())
|
||||
assertThat(pathFilter).isNotNull
|
||||
assertThat(pathFilter?.isIgnored(Paths.get("/one/path"))).isFalse
|
||||
assertThat(pathFilter?.isIgnored(Paths.get("/two/path"))).isFalse
|
||||
assertThat(pathFilter?.isIgnored(Paths.get("/three/path"))).isTrue
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parses excludes correctly`() {
|
||||
val pathFilter = PathFilters.of(emptyList(), listOf("**/one/**", "**/two/**"))
|
||||
assertThat(pathFilter).isNotNull
|
||||
assertThat(pathFilter?.isIgnored(Paths.get("/one/path"))).isTrue
|
||||
assertThat(pathFilter?.isIgnored(Paths.get("/two/path"))).isTrue
|
||||
assertThat(pathFilter?.isIgnored(Paths.get("/three/path"))).isFalse
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parses both includes and excludes correctly`() {
|
||||
val pathFilter = PathFilters.of(listOf("**/one/**"), listOf("**/two/**"))
|
||||
assertThat(pathFilter).isNotNull
|
||||
assertThat(pathFilter?.isIgnored(Paths.get("/one/path"))).isFalse
|
||||
assertThat(pathFilter?.isIgnored(Paths.get("/two/path"))).isTrue
|
||||
assertThat(pathFilter?.isIgnored(Paths.get("/three/path"))).isTrue
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `parsing with different separators` {
|
||||
|
||||
@Test
|
||||
fun `should load multiple comma-separated filters with no spaces around commas`() {
|
||||
val filters = CliArgs { excludes = "**/one/**,**/two/**,**/three" }.toSpecFilters()
|
||||
assertSameFiltersIndependentOfSpacingAndSeparater(filters)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should load multiple semicolon-separated filters with no spaces around semicolons`() {
|
||||
val filters = CliArgs { excludes = "**/one/**;**/two/**;**/three" }.toSpecFilters()
|
||||
assertSameFiltersIndependentOfSpacingAndSeparater(filters)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should load multiple comma-separated filters with spaces around commas`() {
|
||||
val filters = CliArgs { excludes = "**/one/** ,**/two/**, **/three" }.toSpecFilters()
|
||||
assertSameFiltersIndependentOfSpacingAndSeparater(filters)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should load multiple semicolon-separated filters with spaces around semicolons`() {
|
||||
val filters = CliArgs { excludes = "**/one/** ;**/two/**; **/three" }.toSpecFilters()
|
||||
assertSameFiltersIndependentOfSpacingAndSeparater(filters)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should load multiple mixed-separated filters with no spaces around separators`() {
|
||||
val filters = CliArgs { excludes = "**/one/**,**/two/**;**/three" }.toSpecFilters()
|
||||
assertSameFiltersIndependentOfSpacingAndSeparater(filters)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should load multiple mixed-separated filters with spaces around separators`() {
|
||||
val filters = CliArgs { excludes = "**/one/** ,**/two/**; **/three" }.toSpecFilters()
|
||||
assertSameFiltersIndependentOfSpacingAndSeparater(filters)
|
||||
}
|
||||
fun `returns an empty path filter when includes are empty and excludes are empty`() {
|
||||
val pathFilter = PathFilters.of(emptyList(), emptyList())
|
||||
assertThat(pathFilter).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should ignore empty and blank filters`() {
|
||||
val filters = CliArgs { excludes = " ,,**/three" }.toSpecFilters()
|
||||
assertThat(filters?.isIgnored(Paths.get("/three"))).isTrue()
|
||||
assertThat(filters?.isIgnored(Paths.get("/root/three"))).isTrue()
|
||||
assertThat(filters?.isIgnored(Paths.get("/one/path"))).isFalse()
|
||||
assertThat(filters?.isIgnored(Paths.get("/two/path"))).isFalse()
|
||||
assertThat(filters?.isIgnored(Paths.get("/three/path"))).isFalse()
|
||||
fun `parses includes correctly`() {
|
||||
val pathFilter = PathFilters.of(listOf("**/one/**", "**/two/**"), emptyList())
|
||||
assertThat(pathFilter).isNotNull
|
||||
assertThat(pathFilter?.isIgnored(Paths.get("/one/path"))).isFalse
|
||||
assertThat(pathFilter?.isIgnored(Paths.get("/two/path"))).isFalse
|
||||
assertThat(pathFilter?.isIgnored(Paths.get("/three/path"))).isTrue
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parses excludes correctly`() {
|
||||
val pathFilter = PathFilters.of(emptyList(), listOf("**/one/**", "**/two/**"))
|
||||
assertThat(pathFilter).isNotNull
|
||||
assertThat(pathFilter?.isIgnored(Paths.get("/one/path"))).isTrue
|
||||
assertThat(pathFilter?.isIgnored(Paths.get("/two/path"))).isTrue
|
||||
assertThat(pathFilter?.isIgnored(Paths.get("/three/path"))).isFalse
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parses both includes and excludes correctly`() {
|
||||
val pathFilter = PathFilters.of(listOf("**/one/**"), listOf("**/two/**"))
|
||||
assertThat(pathFilter).isNotNull
|
||||
assertThat(pathFilter?.isIgnored(Paths.get("/one/path"))).isFalse
|
||||
assertThat(pathFilter?.isIgnored(Paths.get("/two/path"))).isTrue
|
||||
assertThat(pathFilter?.isIgnored(Paths.get("/three/path"))).isTrue
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `parsing with different separators` {
|
||||
|
||||
@Test
|
||||
fun `should load multiple comma-separated filters with no spaces around commas`() {
|
||||
val filters = CliArgs { excludes = "**/one/**,**/two/**,**/three" }.toSpecFilters()
|
||||
assertSameFiltersIndependentOfSpacingAndSeparater(filters)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should load multiple semicolon-separated filters with no spaces around semicolons`() {
|
||||
val filters = CliArgs { excludes = "**/one/**;**/two/**;**/three" }.toSpecFilters()
|
||||
assertSameFiltersIndependentOfSpacingAndSeparater(filters)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should load multiple comma-separated filters with spaces around commas`() {
|
||||
val filters = CliArgs { excludes = "**/one/** ,**/two/**, **/three" }.toSpecFilters()
|
||||
assertSameFiltersIndependentOfSpacingAndSeparater(filters)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should load multiple semicolon-separated filters with spaces around semicolons`() {
|
||||
val filters = CliArgs { excludes = "**/one/** ;**/two/**; **/three" }.toSpecFilters()
|
||||
assertSameFiltersIndependentOfSpacingAndSeparater(filters)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should load multiple mixed-separated filters with no spaces around separators`() {
|
||||
val filters = CliArgs { excludes = "**/one/**,**/two/**;**/three" }.toSpecFilters()
|
||||
assertSameFiltersIndependentOfSpacingAndSeparater(filters)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should load multiple mixed-separated filters with spaces around separators`() {
|
||||
val filters = CliArgs { excludes = "**/one/** ,**/two/**; **/three" }.toSpecFilters()
|
||||
assertSameFiltersIndependentOfSpacingAndSeparater(filters)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should ignore empty and blank filters`() {
|
||||
val filters = CliArgs { excludes = " ,,**/three" }.toSpecFilters()
|
||||
assertThat(filters?.isIgnored(Paths.get("/three"))).isTrue()
|
||||
assertThat(filters?.isIgnored(Paths.get("/root/three"))).isTrue()
|
||||
assertThat(filters?.isIgnored(Paths.get("/one/path"))).isFalse()
|
||||
assertThat(filters?.isIgnored(Paths.get("/two/path"))).isFalse()
|
||||
assertThat(filters?.isIgnored(Paths.get("/three/path"))).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,109 +12,106 @@ import java.nio.file.Paths
|
||||
|
||||
class ReportPathSpec {
|
||||
|
||||
@EnabledOnOs(OS.WINDOWS)
|
||||
@Nested
|
||||
inner class `report paths` {
|
||||
@EnabledOnOs(OS.WINDOWS)
|
||||
@Nested
|
||||
inner class `a Windows path` {
|
||||
@Test
|
||||
fun `parses a valid absolute path correctly`() {
|
||||
val reportPath = ReportPath.from("test:C:\\tmp\\valid\\report")
|
||||
inner class `a Windows path` {
|
||||
@Test
|
||||
fun `parses a valid absolute path correctly`() {
|
||||
val reportPath = ReportPath.from("test:C:\\tmp\\valid\\report")
|
||||
|
||||
assertThat(reportPath.path).isEqualTo(Paths.get("C:\\tmp\\valid\\report"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parses a valid relative path correctly`() {
|
||||
val reportPath = ReportPath.from("test:valid\\report")
|
||||
|
||||
assertThat(reportPath.path).isEqualTo(Paths.get("valid\\report"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fails when the path is empty`() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy { ReportPath.from("test:") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fails when the path is malformed`() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy { ReportPath.from("test:a*a") }
|
||||
}
|
||||
assertThat(reportPath.path).isEqualTo(Paths.get("C:\\tmp\\valid\\report"))
|
||||
}
|
||||
|
||||
@DisabledOnOs(OS.WINDOWS)
|
||||
@Nested
|
||||
inner class `a POSIX path` {
|
||||
@Test
|
||||
fun `parses a valid absolute path correctly`() {
|
||||
val reportPath = ReportPath.from("test:/tmp/valid/report")
|
||||
@Test
|
||||
fun `parses a valid relative path correctly`() {
|
||||
val reportPath = ReportPath.from("test:valid\\report")
|
||||
|
||||
assertThat(reportPath.path).isEqualTo(Paths.get("/tmp/valid/report"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parses a valid relative path correctly`() {
|
||||
val reportPath = ReportPath.from("test:valid/report")
|
||||
|
||||
assertThat(reportPath.path).isEqualTo(Paths.get("valid/report"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fails when the path is empty`() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy { ReportPath.from("test:") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fails when the path is malformed`() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy { ReportPath.from("test:a${0.toChar()}a") }
|
||||
}
|
||||
assertThat(reportPath.path).isEqualTo(Paths.get("valid\\report"))
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `_kind_ processing` {
|
||||
@Test
|
||||
fun `parses and maps the txt kind correctly`() {
|
||||
val reportPath = ReportPath.from("txt:/tmp/valid/report")
|
||||
@Test
|
||||
fun `fails when the path is empty`() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy { ReportPath.from("test:") }
|
||||
}
|
||||
|
||||
assertThat(reportPath.kind).isEqualTo("txt")
|
||||
}
|
||||
@Test
|
||||
fun `fails when the path is malformed`() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy { ReportPath.from("test:a*a") }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parses and maps the xml kind correctly`() {
|
||||
val reportPath = ReportPath.from("xml:/tmp/valid/report")
|
||||
@DisabledOnOs(OS.WINDOWS)
|
||||
@Nested
|
||||
inner class `a POSIX path` {
|
||||
@Test
|
||||
fun `parses a valid absolute path correctly`() {
|
||||
val reportPath = ReportPath.from("test:/tmp/valid/report")
|
||||
|
||||
assertThat(reportPath.kind).isEqualTo("xml")
|
||||
}
|
||||
assertThat(reportPath.path).isEqualTo(Paths.get("/tmp/valid/report"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parses and maps the html kind correctly`() {
|
||||
val reportPath = ReportPath.from("html:/tmp/valid/report")
|
||||
@Test
|
||||
fun `parses a valid relative path correctly`() {
|
||||
val reportPath = ReportPath.from("test:valid/report")
|
||||
|
||||
assertThat(reportPath.kind).isEqualTo("html")
|
||||
}
|
||||
assertThat(reportPath.path).isEqualTo(Paths.get("valid/report"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parses a non-default kind correctly`() {
|
||||
val reportPath = ReportPath.from("test:/tmp/valid/report")
|
||||
@Test
|
||||
fun `fails when the path is empty`() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy { ReportPath.from("test:") }
|
||||
}
|
||||
|
||||
assertThat(reportPath.kind).isEqualTo("test")
|
||||
}
|
||||
@Test
|
||||
fun `fails when the path is malformed`() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy { ReportPath.from("test:a${0.toChar()}a") }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fails when the kind is empty`() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy { ReportPath.from(":/tmp/anything") }
|
||||
}
|
||||
@Nested
|
||||
inner class `_kind_ processing` {
|
||||
@Test
|
||||
fun `parses and maps the txt kind correctly`() {
|
||||
val reportPath = ReportPath.from("txt:/tmp/valid/report")
|
||||
|
||||
@Test
|
||||
fun `fails when part size is illegal`() {
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy { ReportPath.from("") }
|
||||
}
|
||||
assertThat(reportPath.kind).isEqualTo("txt")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parses and maps the xml kind correctly`() {
|
||||
val reportPath = ReportPath.from("xml:/tmp/valid/report")
|
||||
|
||||
assertThat(reportPath.kind).isEqualTo("xml")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parses and maps the html kind correctly`() {
|
||||
val reportPath = ReportPath.from("html:/tmp/valid/report")
|
||||
|
||||
assertThat(reportPath.kind).isEqualTo("html")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parses a non-default kind correctly`() {
|
||||
val reportPath = ReportPath.from("test:/tmp/valid/report")
|
||||
|
||||
assertThat(reportPath.kind).isEqualTo("test")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fails when the kind is empty`() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy { ReportPath.from(":/tmp/anything") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fails when part size is illegal`() {
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy { ReportPath.from("") }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,49 +12,45 @@ import java.io.PrintStream
|
||||
|
||||
class AstPrinterSpec {
|
||||
|
||||
val path = resourceAsPath("cases").toString()
|
||||
|
||||
@Nested
|
||||
inner class `element printer` {
|
||||
|
||||
val path = resourceAsPath("cases").toString()
|
||||
|
||||
@Nested
|
||||
inner class `successful AST printing` {
|
||||
|
||||
@Test
|
||||
fun `should print the AST as string`() {
|
||||
val output = ByteArrayOutputStream()
|
||||
val args = CliArgs()
|
||||
args.input = resourceAsPath("cases/Poko.kt").toString()
|
||||
val printer = AstPrinter(args, PrintStream(output))
|
||||
|
||||
printer.execute()
|
||||
|
||||
assertThat(output.toString()).isNotEmpty()
|
||||
}
|
||||
}
|
||||
inner class `successful AST printing` {
|
||||
|
||||
@Test
|
||||
fun `throws an exception when declaring multiple input files`() {
|
||||
val multiplePaths = "$path,$path"
|
||||
fun `should print the AST as string`() {
|
||||
val output = ByteArrayOutputStream()
|
||||
val args = CliArgs()
|
||||
args.input = multiplePaths
|
||||
val printer = AstPrinter(args, NullPrintStream())
|
||||
args.input = resourceAsPath("cases/Poko.kt").toString()
|
||||
val printer = AstPrinter(args, PrintStream(output))
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy { printer.execute() }
|
||||
.withMessage("More than one input path specified. Printing AST is only supported for single files.")
|
||||
}
|
||||
printer.execute()
|
||||
|
||||
@Test
|
||||
fun `throws an exception when trying to print the AST of a directory`() {
|
||||
val args = CliArgs()
|
||||
args.input = path
|
||||
val printer = AstPrinter(args, NullPrintStream())
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy { printer.execute() }
|
||||
.withMessageStartingWith("Input path ")
|
||||
.withMessageEndingWith(" must be a kotlin file and not a directory.")
|
||||
assertThat(output.toString()).isNotEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `throws an exception when declaring multiple input files`() {
|
||||
val multiplePaths = "$path,$path"
|
||||
val args = CliArgs()
|
||||
args.input = multiplePaths
|
||||
val printer = AstPrinter(args, NullPrintStream())
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy { printer.execute() }
|
||||
.withMessage("More than one input path specified. Printing AST is only supported for single files.")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `throws an exception when trying to print the AST of a directory`() {
|
||||
val args = CliArgs()
|
||||
args.input = path
|
||||
val printer = AstPrinter(args, NullPrintStream())
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy { printer.execute() }
|
||||
.withMessageStartingWith("Input path ")
|
||||
.withMessageEndingWith(" must be a kotlin file and not a directory.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,23 +3,18 @@ package io.gitlab.arturbosch.detekt.cli.runners
|
||||
import io.github.detekt.test.utils.compileForTest
|
||||
import io.github.detekt.test.utils.resourceAsPath
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class ElementPrinterSpec {
|
||||
|
||||
@Nested
|
||||
inner class `element printer` {
|
||||
@Test
|
||||
fun `should print the ast as string`() {
|
||||
val case = resourceAsPath("cases/Poko.kt")
|
||||
val ktFile = compileForTest(case)
|
||||
|
||||
@Test
|
||||
fun `should print the ast as string`() {
|
||||
val case = resourceAsPath("cases/Poko.kt")
|
||||
val ktFile = compileForTest(case)
|
||||
val dump = ElementPrinter.dump(ktFile)
|
||||
|
||||
val dump = ElementPrinter.dump(ktFile)
|
||||
|
||||
assertThat(dump.trimIndent()).isEqualTo(expected)
|
||||
}
|
||||
assertThat(dump.trimIndent()).isEqualTo(expected)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,47 +11,42 @@ import io.gitlab.arturbosch.detekt.api.Severity
|
||||
import io.gitlab.arturbosch.detekt.test.yamlConfig
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class CorrectableRulesFirstSpec {
|
||||
|
||||
@Nested
|
||||
inner class `correctable rules are run first` {
|
||||
@Test
|
||||
fun `runs rule with id 'NonCorrectable' last`() {
|
||||
var actualLastRuleId = ""
|
||||
|
||||
@Test
|
||||
fun `runs rule with id 'NonCorrectable' last`() {
|
||||
var actualLastRuleId = ""
|
||||
|
||||
class First(config: Config) : Rule(config) {
|
||||
override val issue: Issue = justAnIssue.copy(id = "NonCorrectable")
|
||||
override fun visitClass(klass: KtClass) {
|
||||
actualLastRuleId = issue.id
|
||||
}
|
||||
class First(config: Config) : Rule(config) {
|
||||
override val issue: Issue = justAnIssue.copy(id = "NonCorrectable")
|
||||
override fun visitClass(klass: KtClass) {
|
||||
actualLastRuleId = issue.id
|
||||
}
|
||||
|
||||
class Last(config: Config) : Rule(config) {
|
||||
override val issue: Issue = justAnIssue.copy(id = "Correctable")
|
||||
override fun visitClass(klass: KtClass) {
|
||||
actualLastRuleId = issue.id
|
||||
}
|
||||
}
|
||||
|
||||
val testFile = path.resolve("Test.kt")
|
||||
val settings = createProcessingSettings(testFile, yamlConfig("configs/one-correctable-rule.yml"))
|
||||
val detector = Analyzer(
|
||||
settings,
|
||||
listOf(object : RuleSetProvider {
|
||||
override val ruleSetId: String = "Test"
|
||||
override fun instance(config: Config) = RuleSet(ruleSetId, listOf(Last(config), First(config)))
|
||||
}),
|
||||
emptyList()
|
||||
)
|
||||
|
||||
settings.use { detector.run(listOf(compileForTest(testFile))) }
|
||||
|
||||
assertThat(actualLastRuleId).isEqualTo("NonCorrectable")
|
||||
}
|
||||
|
||||
class Last(config: Config) : Rule(config) {
|
||||
override val issue: Issue = justAnIssue.copy(id = "Correctable")
|
||||
override fun visitClass(klass: KtClass) {
|
||||
actualLastRuleId = issue.id
|
||||
}
|
||||
}
|
||||
|
||||
val testFile = path.resolve("Test.kt")
|
||||
val settings = createProcessingSettings(testFile, yamlConfig("configs/one-correctable-rule.yml"))
|
||||
val detector = Analyzer(
|
||||
settings,
|
||||
listOf(object : RuleSetProvider {
|
||||
override val ruleSetId: String = "Test"
|
||||
override fun instance(config: Config) = RuleSet(ruleSetId, listOf(Last(config), First(config)))
|
||||
}),
|
||||
emptyList()
|
||||
)
|
||||
|
||||
settings.use { detector.run(listOf(compileForTest(testFile))) }
|
||||
|
||||
assertThat(actualLastRuleId).isEqualTo("NonCorrectable")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import io.github.detekt.test.utils.resourceAsPath
|
||||
import io.gitlab.arturbosch.detekt.core.rules.RuleSetLocator
|
||||
import io.gitlab.arturbosch.detekt.core.tooling.withSettings
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
/**
|
||||
@@ -20,22 +19,18 @@ import org.junit.jupiter.api.Test
|
||||
*/
|
||||
class CustomRuleSetProviderSpec {
|
||||
|
||||
@Nested
|
||||
inner class `custom rule sets should be loadable through jars` {
|
||||
|
||||
@Test
|
||||
fun `should load the sample provider`() {
|
||||
val sampleRuleSet = resourceAsPath("sample-rule-set.jar")
|
||||
val spec = createNullLoggingSpec {
|
||||
extensions {
|
||||
disableDefaultRuleSets = true
|
||||
fromPaths { listOf(sampleRuleSet) }
|
||||
}
|
||||
@Test
|
||||
fun `custom rule sets should be loadable through jars should load the sample provider`() {
|
||||
val sampleRuleSet = resourceAsPath("sample-rule-set.jar")
|
||||
val spec = createNullLoggingSpec {
|
||||
extensions {
|
||||
disableDefaultRuleSets = true
|
||||
fromPaths { listOf(sampleRuleSet) }
|
||||
}
|
||||
|
||||
val providers = spec.withSettings { RuleSetLocator(this).load() }
|
||||
|
||||
assertThat(providers).filteredOn { it.ruleSetId == "sample" }.hasSize(1)
|
||||
}
|
||||
|
||||
val providers = spec.withSettings { RuleSetLocator(this).load() }
|
||||
|
||||
assertThat(providers).filteredOn { it.ruleSetId == "sample" }.hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import io.gitlab.arturbosch.detekt.api.FileProcessListener
|
||||
import io.gitlab.arturbosch.detekt.test.yamlConfig
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.fail
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.reflections.Reflections
|
||||
import java.lang.reflect.Modifier
|
||||
@@ -15,28 +14,24 @@ import java.lang.reflect.Modifier
|
||||
*/
|
||||
class FileProcessorLocatorSpec {
|
||||
|
||||
@Nested
|
||||
inner class `file processor locator` {
|
||||
private val path = resourceAsPath("")
|
||||
|
||||
val path = resourceAsPath("")
|
||||
@Test
|
||||
fun `contains all processors`() {
|
||||
val processors = createProcessingSettings(path).use { FileProcessorLocator(it).load() }
|
||||
val processorClasses = getProcessorClasses()
|
||||
|
||||
@Test
|
||||
fun `contains all processors`() {
|
||||
val processors = createProcessingSettings(path).use { FileProcessorLocator(it).load() }
|
||||
val processorClasses = getProcessorClasses()
|
||||
assertThat(processorClasses).isNotEmpty
|
||||
processorClasses
|
||||
.filter { clazz -> processors.none { clazz == it.javaClass } }
|
||||
.forEach { fail("$it processor is not loaded by the FileProcessorLocator") }
|
||||
}
|
||||
|
||||
assertThat(processorClasses).isNotEmpty
|
||||
processorClasses
|
||||
.filter { clazz -> processors.none { clazz == it.javaClass } }
|
||||
.forEach { fail("$it processor is not loaded by the FileProcessorLocator") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `has disabled processors`() {
|
||||
val config = yamlConfig("configs/disabled-processors.yml")
|
||||
val processors = createProcessingSettings(path, config).use { FileProcessorLocator(it).load() }
|
||||
assertThat(processors).isEmpty()
|
||||
}
|
||||
@Test
|
||||
fun `has disabled processors`() {
|
||||
val config = yamlConfig("configs/disabled-processors.yml")
|
||||
val processors = createProcessingSettings(path, config).use { FileProcessorLocator(it).load() }
|
||||
assertThat(processors).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,80 +5,75 @@ import io.gitlab.arturbosch.detekt.core.tooling.withSettings
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.nio.file.Paths
|
||||
|
||||
class KtTreeCompilerSpec {
|
||||
|
||||
@Nested
|
||||
inner class `tree compiler functionality` {
|
||||
@Test
|
||||
fun `should compile all files`() {
|
||||
val (ktFiles, _) = fixture { compile(path) }
|
||||
assertThat(ktFiles.size)
|
||||
.describedAs("It should compile at least three files, but did ${ktFiles.size}")
|
||||
.isGreaterThanOrEqualTo(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should compile all files`() {
|
||||
val (ktFiles, _) = fixture { compile(path) }
|
||||
assertThat(ktFiles.size)
|
||||
.describedAs("It should compile at least three files, but did ${ktFiles.size}")
|
||||
.isGreaterThanOrEqualTo(3)
|
||||
}
|
||||
@Test
|
||||
fun `should filter the file 'Default_kt'`() {
|
||||
val (ktFiles, output) = fixture("**/Default.kt", loggingDebug = true) { compile(path) }
|
||||
val ktFile = ktFiles.find { it.name == "Default.kt" }
|
||||
assertThat(ktFile).describedAs("It should have no Default.kt file").isNull()
|
||||
|
||||
@Test
|
||||
fun `should filter the file 'Default_kt'`() {
|
||||
val (ktFiles, output) = fixture("**/Default.kt", loggingDebug = true) { compile(path) }
|
||||
val ktFile = ktFiles.find { it.name == "Default.kt" }
|
||||
assertThat(ktFile).describedAs("It should have no Default.kt file").isNull()
|
||||
assertThat(output).contains("Ignoring file ")
|
||||
}
|
||||
|
||||
assertThat(output).contains("Ignoring file ")
|
||||
}
|
||||
@Test
|
||||
fun `should work with two or more filters`() {
|
||||
val (ktFiles, _) = fixture(
|
||||
"**/Default.kt",
|
||||
"**/*Test*",
|
||||
"**/*Complex*",
|
||||
"**/*KotlinScript*"
|
||||
) { compile(path) }
|
||||
assertThat(ktFiles).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should work with two or more filters`() {
|
||||
val (ktFiles, _) = fixture(
|
||||
"**/Default.kt",
|
||||
"**/*Test*",
|
||||
"**/*Complex*",
|
||||
"**/*KotlinScript*"
|
||||
) { compile(path) }
|
||||
assertThat(ktFiles).isEmpty()
|
||||
}
|
||||
@Test
|
||||
fun `should also compile regular files`() {
|
||||
val (ktFiles, _) = fixture { compile(path.resolve("Default.kt")) }
|
||||
assertThat(ktFiles.size).isEqualTo(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should also compile regular files`() {
|
||||
val (ktFiles, _) = fixture { compile(path.resolve("Default.kt")) }
|
||||
assertThat(ktFiles.size).isEqualTo(1)
|
||||
}
|
||||
@Test
|
||||
fun `throws an exception if given file does not exist`() {
|
||||
val invalidPath = "NOTHERE"
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy { fixture { compile(Paths.get(invalidPath)) } }
|
||||
.withMessage("Given path $invalidPath does not exist!")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `throws an exception if given file does not exist`() {
|
||||
val invalidPath = "NOTHERE"
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy { fixture { compile(Paths.get(invalidPath)) } }
|
||||
.withMessage("Given path $invalidPath does not exist!")
|
||||
}
|
||||
@Test
|
||||
fun `does not compile a folder with a css file`() {
|
||||
val cssPath = resourceAsPath("css")
|
||||
val (ktFiles, output) = fixture { compile(cssPath) }
|
||||
assertThat(ktFiles).isEmpty()
|
||||
assertThat(output).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not compile a folder with a css file`() {
|
||||
val cssPath = resourceAsPath("css")
|
||||
val (ktFiles, output) = fixture { compile(cssPath) }
|
||||
assertThat(ktFiles).isEmpty()
|
||||
assertThat(output).isEmpty()
|
||||
}
|
||||
@Test
|
||||
fun `does not compile a css file`() {
|
||||
val cssPath = resourceAsPath("css").resolve("test.css")
|
||||
val (ktFiles, output) = fixture { compile(cssPath) }
|
||||
assertThat(ktFiles).isEmpty()
|
||||
assertThat(output).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not compile a css file`() {
|
||||
val cssPath = resourceAsPath("css").resolve("test.css")
|
||||
val (ktFiles, output) = fixture { compile(cssPath) }
|
||||
assertThat(ktFiles).isEmpty()
|
||||
assertThat(output).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not compile a css file but show log if debug is enabled`() {
|
||||
val cssPath = resourceAsPath("css").resolve("test.css")
|
||||
val (ktFiles, output) = fixture(loggingDebug = true) { compile(cssPath) }
|
||||
assertThat(ktFiles).isEmpty()
|
||||
assertThat(output).contains("Ignoring a file detekt cannot handle: ")
|
||||
}
|
||||
@Test
|
||||
fun `does not compile a css file but show log if debug is enabled`() {
|
||||
val cssPath = resourceAsPath("css").resolve("test.css")
|
||||
val (ktFiles, output) = fixture(loggingDebug = true) { compile(cssPath) }
|
||||
assertThat(ktFiles).isEmpty()
|
||||
assertThat(output).contains("Ignoring a file detekt cannot handle: ")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,53 +22,48 @@ import org.assertj.core.api.Assertions.assertThat
|
||||
import org.jetbrains.kotlin.psi.KtAnnotation
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class TopLevelAutoCorrectSpec {
|
||||
|
||||
@Nested
|
||||
inner class `autoCorrect_ false on top level` {
|
||||
|
||||
@Test
|
||||
fun `should format the test file but not print the modified content to disc`() {
|
||||
val fileContentBeforeAutoCorrect = readResourceContent("cases/Test.kt")
|
||||
val fileUnderTest = resourceAsPath("cases/Test.kt")
|
||||
val spec = ProcessingSpec {
|
||||
project {
|
||||
inputPaths = listOf(fileUnderTest)
|
||||
}
|
||||
config {
|
||||
resources = listOf(resourceUrl("configs/rule-and-ruleset-autocorrect-true.yaml"))
|
||||
}
|
||||
rules {
|
||||
autoCorrect = false // fixture
|
||||
}
|
||||
logging {
|
||||
outputChannel = NullPrintStream()
|
||||
errorChannel = NullPrintStream()
|
||||
}
|
||||
@Test
|
||||
fun `should format the test file but not print the modified content to disc`() {
|
||||
val fileContentBeforeAutoCorrect = readResourceContent("cases/Test.kt")
|
||||
val fileUnderTest = resourceAsPath("cases/Test.kt")
|
||||
val spec = ProcessingSpec {
|
||||
project {
|
||||
inputPaths = listOf(fileUnderTest)
|
||||
}
|
||||
|
||||
val contentChangedListener = object : FileProcessListener {
|
||||
override fun onFinish(files: List<KtFile>, result: Detektion, bindingContext: BindingContext) {
|
||||
assertThat(files).hasSize(1)
|
||||
assertThat(files[0].text).isNotEqualToIgnoringWhitespace(fileContentBeforeAutoCorrect)
|
||||
}
|
||||
config {
|
||||
resources = listOf(resourceUrl("configs/rule-and-ruleset-autocorrect-true.yaml"))
|
||||
}
|
||||
|
||||
AnalysisFacade(spec).runAnalysis {
|
||||
DefaultLifecycle(
|
||||
mockk(),
|
||||
it,
|
||||
inputPathsToKtFiles,
|
||||
processorsProvider = { listOf(contentChangedListener) },
|
||||
ruleSetsProvider = { listOf(TopLevelAutoCorrectProvider()) }
|
||||
)
|
||||
rules {
|
||||
autoCorrect = false // fixture
|
||||
}
|
||||
logging {
|
||||
outputChannel = NullPrintStream()
|
||||
errorChannel = NullPrintStream()
|
||||
}
|
||||
|
||||
assertThat(readResourceContent("cases/Test.kt")).isEqualTo(fileContentBeforeAutoCorrect)
|
||||
}
|
||||
|
||||
val contentChangedListener = object : FileProcessListener {
|
||||
override fun onFinish(files: List<KtFile>, result: Detektion, bindingContext: BindingContext) {
|
||||
assertThat(files).hasSize(1)
|
||||
assertThat(files[0].text).isNotEqualToIgnoringWhitespace(fileContentBeforeAutoCorrect)
|
||||
}
|
||||
}
|
||||
|
||||
AnalysisFacade(spec).runAnalysis {
|
||||
DefaultLifecycle(
|
||||
mockk(),
|
||||
it,
|
||||
inputPathsToKtFiles,
|
||||
processorsProvider = { listOf(contentChangedListener) },
|
||||
ruleSetsProvider = { listOf(TopLevelAutoCorrectProvider()) }
|
||||
)
|
||||
}
|
||||
|
||||
assertThat(readResourceContent("cases/Test.kt")).isEqualTo(fileContentBeforeAutoCorrect)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,85 +6,80 @@ import io.gitlab.arturbosch.detekt.test.TestDetektion
|
||||
import io.gitlab.arturbosch.detekt.test.createFinding
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.nio.file.Files
|
||||
|
||||
class BaselineFacadeSpec {
|
||||
|
||||
@Nested
|
||||
inner class `a baseline facade` {
|
||||
private val baselineFile = createTempDirectoryForTest("baseline_format").resolve("baseline.xml")
|
||||
private val validBaseline = resourceAsPath("/baseline_feature/valid-baseline.xml")
|
||||
|
||||
private val baselineFile = createTempDirectoryForTest("baseline_format").resolve("baseline.xml")
|
||||
private val validBaseline = resourceAsPath("/baseline_feature/valid-baseline.xml")
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
Files.deleteIfExists(baselineFile)
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
Files.deleteIfExists(baselineFile)
|
||||
}
|
||||
@Test
|
||||
fun `returns a BaselineFilteredResult when the baseline exists`() {
|
||||
val detektion = BaselineFacade().transformResult(validBaseline, TestDetektion())
|
||||
|
||||
@Test
|
||||
fun `returns a BaselineFilteredResult when the baseline exists`() {
|
||||
val detektion = BaselineFacade().transformResult(validBaseline, TestDetektion())
|
||||
assertThat(detektion).isInstanceOf(BaselineFilteredResult::class.java)
|
||||
}
|
||||
|
||||
assertThat(detektion).isInstanceOf(BaselineFilteredResult::class.java)
|
||||
}
|
||||
@Test
|
||||
fun `returns the same detektion when the baseline doesn't exist`() {
|
||||
val initialDetektion = TestDetektion()
|
||||
val detektion = BaselineFacade().transformResult(baselineFile, initialDetektion)
|
||||
|
||||
@Test
|
||||
fun `returns the same detektion when the baseline doesn't exist`() {
|
||||
val initialDetektion = TestDetektion()
|
||||
val detektion = BaselineFacade().transformResult(baselineFile, initialDetektion)
|
||||
assertThat(detektion).isEqualTo(initialDetektion)
|
||||
}
|
||||
|
||||
assertThat(detektion).isEqualTo(initialDetektion)
|
||||
}
|
||||
@Test
|
||||
fun `doesn't create a baseline file without findings`() {
|
||||
BaselineFacade().createOrUpdate(baselineFile, emptyList())
|
||||
|
||||
@Test
|
||||
fun `doesn't create a baseline file without findings`() {
|
||||
BaselineFacade().createOrUpdate(baselineFile, emptyList())
|
||||
assertThat(baselineFile).doesNotExist()
|
||||
}
|
||||
|
||||
assertThat(baselineFile).doesNotExist()
|
||||
}
|
||||
@Test
|
||||
fun `creates on top of an existing a baseline file without findings`() {
|
||||
Files.copy(validBaseline, baselineFile)
|
||||
|
||||
@Test
|
||||
fun `creates on top of an existing a baseline file without findings`() {
|
||||
Files.copy(validBaseline, baselineFile)
|
||||
BaselineFacade().createOrUpdate(baselineFile, emptyList())
|
||||
|
||||
BaselineFacade().createOrUpdate(baselineFile, emptyList())
|
||||
assertThat(baselineFile).hasContent(
|
||||
"""
|
||||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues>
|
||||
<ID>LongParameterList:Signature</ID>
|
||||
<ID>LongMethod:Signature</ID>
|
||||
</ManuallySuppressedIssues>
|
||||
<CurrentIssues></CurrentIssues>
|
||||
</SmellBaseline>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
assertThat(baselineFile).hasContent(
|
||||
"""
|
||||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues>
|
||||
<ID>LongParameterList:Signature</ID>
|
||||
<ID>LongMethod:Signature</ID>
|
||||
</ManuallySuppressedIssues>
|
||||
<CurrentIssues></CurrentIssues>
|
||||
</SmellBaseline>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
@Test
|
||||
fun `creates on top of an existing a baseline file with findings`() {
|
||||
Files.copy(validBaseline, baselineFile)
|
||||
|
||||
@Test
|
||||
fun `creates on top of an existing a baseline file with findings`() {
|
||||
Files.copy(validBaseline, baselineFile)
|
||||
BaselineFacade().createOrUpdate(baselineFile, listOf(createFinding()))
|
||||
|
||||
BaselineFacade().createOrUpdate(baselineFile, listOf(createFinding()))
|
||||
|
||||
assertThat(baselineFile).hasContent(
|
||||
"""
|
||||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues>
|
||||
<ID>LongParameterList:Signature</ID>
|
||||
<ID>LongMethod:Signature</ID>
|
||||
</ManuallySuppressedIssues>
|
||||
<CurrentIssues>
|
||||
<ID>TestSmell:TestEntitySignature</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
assertThat(baselineFile).hasContent(
|
||||
"""
|
||||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues>
|
||||
<ID>LongParameterList:Signature</ID>
|
||||
<ID>LongMethod:Signature</ID>
|
||||
</ManuallySuppressedIssues>
|
||||
<CurrentIssues>
|
||||
<ID>TestSmell:TestEntitySignature</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,44 +5,39 @@ import io.gitlab.arturbosch.detekt.test.TestDetektion
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class BaselineFilteredResultSpec {
|
||||
|
||||
@Nested
|
||||
inner class `baseline based result transformation` {
|
||||
private val baselineFile = resourceAsPath("/baseline_feature/valid-baseline.xml")
|
||||
|
||||
private val baselineFile = resourceAsPath("/baseline_feature/valid-baseline.xml")
|
||||
private val result = TestDetektion(
|
||||
mockk {
|
||||
every { id }.returns("LongParameterList")
|
||||
every { signature }.returns("Signature")
|
||||
},
|
||||
mockk {
|
||||
every { id }.returns("LongMethod")
|
||||
every { signature }.returns("Signature")
|
||||
},
|
||||
mockk {
|
||||
every { id }.returns("FeatureEnvy")
|
||||
every { signature }.returns("Signature")
|
||||
},
|
||||
)
|
||||
|
||||
private val result = TestDetektion(
|
||||
mockk {
|
||||
every { id }.returns("LongParameterList")
|
||||
every { signature }.returns("Signature")
|
||||
},
|
||||
mockk {
|
||||
every { id }.returns("LongMethod")
|
||||
every { signature }.returns("Signature")
|
||||
},
|
||||
mockk {
|
||||
every { id }.returns("FeatureEnvy")
|
||||
every { signature }.returns("Signature")
|
||||
},
|
||||
)
|
||||
@Test
|
||||
fun `does return the same finding on empty baseline`() {
|
||||
val actual = BaselineFilteredResult(result, Baseline(emptySet(), emptySet()))
|
||||
assertThat(actual.findings).hasSize(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does return the same finding on empty baseline`() {
|
||||
val actual = BaselineFilteredResult(result, Baseline(emptySet(), emptySet()))
|
||||
assertThat(actual.findings).hasSize(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `filters with an existing baseline file`() {
|
||||
val baseline = Baseline.load(baselineFile)
|
||||
val actual = BaselineFilteredResult(result, baseline)
|
||||
// Note: Detektion works with Map<RuleSetId, List<Finding>
|
||||
// but the TestDetektion maps the RuleId as RuleSetId
|
||||
actual.findings.forEach { (_, value) -> assertThat(value).isEmpty() }
|
||||
}
|
||||
@Test
|
||||
fun `filters with an existing baseline file`() {
|
||||
val baseline = Baseline.load(baselineFile)
|
||||
val actual = BaselineFilteredResult(result, baseline)
|
||||
// Note: Detektion works with Map<RuleSetId, List<Finding>
|
||||
// but the TestDetektion maps the RuleId as RuleSetId
|
||||
actual.findings.forEach { (_, value) -> assertThat(value).isEmpty() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,78 +12,74 @@ import java.nio.file.Files
|
||||
class BaselineFormatSpec {
|
||||
|
||||
@Nested
|
||||
inner class `baseline format` {
|
||||
inner class `read a baseline file` {
|
||||
|
||||
@Nested
|
||||
inner class `read a baseline file` {
|
||||
@Test
|
||||
fun `loads the baseline file`() {
|
||||
val path = resourceAsPath("/baseline_feature/valid-baseline.xml")
|
||||
val (manuallySuppressedIssues, currentIssues) = BaselineFormat().read(path)
|
||||
|
||||
@Test
|
||||
fun `loads the baseline file`() {
|
||||
val path = resourceAsPath("/baseline_feature/valid-baseline.xml")
|
||||
val (manuallySuppressedIssues, currentIssues) = BaselineFormat().read(path)
|
||||
|
||||
assertThat(manuallySuppressedIssues).hasSize(2)
|
||||
assertThat(manuallySuppressedIssues).anySatisfy { it.startsWith("LongParameterList") }
|
||||
assertThat(manuallySuppressedIssues).anySatisfy { it.startsWith("LongMethod") }
|
||||
assertThat(currentIssues).hasSize(1)
|
||||
assertThat(currentIssues).anySatisfy { it.startsWith("FeatureEnvy") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `throws on an invalid baseline file extension`() {
|
||||
val path = resourceAsPath("/baseline_feature/invalid-txt-baseline.txt")
|
||||
assertThatThrownBy { BaselineFormat().read(path) }
|
||||
.isInstanceOf(BaselineFormat.InvalidState::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `throws on an invalid baseline ID declaration`() {
|
||||
val path = resourceAsPath("/baseline_feature/missing-temporary-suppressed-baseline.xml")
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy { BaselineFormat().read(path) }
|
||||
.withMessage("The content of the ID element must not be empty")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `supports deprecated baseline values`() {
|
||||
val path = resourceAsPath("/baseline_feature/deprecated-baseline.xml")
|
||||
val (manuallySuppressedIssues, currentIssues) = BaselineFormat().read(path)
|
||||
|
||||
assertThat(manuallySuppressedIssues).hasSize(2)
|
||||
assertThat(manuallySuppressedIssues).anySatisfy { it.startsWith("LongParameterList") }
|
||||
assertThat(manuallySuppressedIssues).anySatisfy { it.startsWith("LongMethod") }
|
||||
assertThat(currentIssues).hasSize(1)
|
||||
assertThat(currentIssues).anySatisfy { it.startsWith("FeatureEnvy") }
|
||||
}
|
||||
assertThat(manuallySuppressedIssues).hasSize(2)
|
||||
assertThat(manuallySuppressedIssues).anySatisfy { it.startsWith("LongParameterList") }
|
||||
assertThat(manuallySuppressedIssues).anySatisfy { it.startsWith("LongMethod") }
|
||||
assertThat(currentIssues).hasSize(1)
|
||||
assertThat(currentIssues).anySatisfy { it.startsWith("FeatureEnvy") }
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `writes a baseline file` {
|
||||
@Test
|
||||
fun `throws on an invalid baseline file extension`() {
|
||||
val path = resourceAsPath("/baseline_feature/invalid-txt-baseline.txt")
|
||||
assertThatThrownBy { BaselineFormat().read(path) }
|
||||
.isInstanceOf(BaselineFormat.InvalidState::class.java)
|
||||
}
|
||||
|
||||
private val savedBaseline = Baseline(setOf("4", "2", "2"), setOf("1", "2", "3"))
|
||||
@Test
|
||||
fun `throws on an invalid baseline ID declaration`() {
|
||||
val path = resourceAsPath("/baseline_feature/missing-temporary-suppressed-baseline.xml")
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy { BaselineFormat().read(path) }
|
||||
.withMessage("The content of the ID element must not be empty")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `has a new line at the end of the written baseline file`() {
|
||||
val tempFile = createTempFileForTest("baseline1", ".xml")
|
||||
@Test
|
||||
fun `supports deprecated baseline values`() {
|
||||
val path = resourceAsPath("/baseline_feature/deprecated-baseline.xml")
|
||||
val (manuallySuppressedIssues, currentIssues) = BaselineFormat().read(path)
|
||||
|
||||
val format = BaselineFormat()
|
||||
format.write(savedBaseline, tempFile)
|
||||
val bytes = Files.readAllBytes(tempFile)
|
||||
val content = String(bytes, Charsets.UTF_8)
|
||||
assertThat(manuallySuppressedIssues).hasSize(2)
|
||||
assertThat(manuallySuppressedIssues).anySatisfy { it.startsWith("LongParameterList") }
|
||||
assertThat(manuallySuppressedIssues).anySatisfy { it.startsWith("LongMethod") }
|
||||
assertThat(currentIssues).hasSize(1)
|
||||
assertThat(currentIssues).anySatisfy { it.startsWith("FeatureEnvy") }
|
||||
}
|
||||
}
|
||||
|
||||
assertThat(content).endsWith(">\n")
|
||||
}
|
||||
@Nested
|
||||
inner class `writes a baseline file` {
|
||||
|
||||
@Test
|
||||
fun `asserts that the saved and loaded baseline files are equal`() {
|
||||
val tempFile = createTempFileForTest("baseline-saved", ".xml")
|
||||
private val savedBaseline = Baseline(setOf("4", "2", "2"), setOf("1", "2", "3"))
|
||||
|
||||
val format = BaselineFormat()
|
||||
format.write(savedBaseline, tempFile)
|
||||
val loadedBaseline = format.read(tempFile)
|
||||
@Test
|
||||
fun `has a new line at the end of the written baseline file`() {
|
||||
val tempFile = createTempFileForTest("baseline1", ".xml")
|
||||
|
||||
assertThat(loadedBaseline).isEqualTo(savedBaseline)
|
||||
}
|
||||
val format = BaselineFormat()
|
||||
format.write(savedBaseline, tempFile)
|
||||
val bytes = Files.readAllBytes(tempFile)
|
||||
val content = String(bytes, Charsets.UTF_8)
|
||||
|
||||
assertThat(content).endsWith(">\n")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `asserts that the saved and loaded baseline files are equal`() {
|
||||
val tempFile = createTempFileForTest("baseline-saved", ".xml")
|
||||
|
||||
val format = BaselineFormat()
|
||||
format.write(savedBaseline, tempFile)
|
||||
val loadedBaseline = format.read(tempFile)
|
||||
|
||||
assertThat(loadedBaseline).isEqualTo(savedBaseline)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import io.mockk.mockk
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.PrintStream
|
||||
import java.net.URI
|
||||
@@ -23,106 +22,102 @@ import java.nio.file.Path
|
||||
@OptIn(UnstableApi::class)
|
||||
class BaselineResultMappingSpec {
|
||||
|
||||
@Nested
|
||||
inner class `a baseline result mapping` {
|
||||
private val dir = createTempDirectoryForTest("baseline_format")
|
||||
private val baselineFile = dir.resolve("baseline.xml")
|
||||
private val existingBaselineFile = resourceAsPath("/baseline_feature/valid-baseline.xml")
|
||||
private lateinit var findings: Map<String, List<Finding>>
|
||||
private lateinit var finding: Finding
|
||||
|
||||
private val dir = createTempDirectoryForTest("baseline_format")
|
||||
private val baselineFile = dir.resolve("baseline.xml")
|
||||
private val existingBaselineFile = resourceAsPath("/baseline_feature/valid-baseline.xml")
|
||||
private lateinit var findings: Map<String, List<Finding>>
|
||||
private lateinit var finding: Finding
|
||||
@BeforeEach
|
||||
fun setupMocks() {
|
||||
finding = mockk()
|
||||
every { finding.id }.returns("SomeIssueId")
|
||||
every { finding.signature }.returns("SomeSignature")
|
||||
findings = mapOf("RuleSet" to listOf(finding))
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
fun setupMocks() {
|
||||
finding = mockk()
|
||||
every { finding.id }.returns("SomeIssueId")
|
||||
every { finding.signature }.returns("SomeSignature")
|
||||
findings = mapOf("RuleSet" to listOf(finding))
|
||||
}
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
Files.deleteIfExists(baselineFile)
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
Files.deleteIfExists(baselineFile)
|
||||
}
|
||||
@Test
|
||||
fun `should not create a new baseline file when no findings occurred`() {
|
||||
val mapping = resultMapping(
|
||||
baselineFile = baselineFile,
|
||||
createBaseline = true,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `should not create a new baseline file when no findings occurred`() {
|
||||
val mapping = resultMapping(
|
||||
baselineFile = baselineFile,
|
||||
createBaseline = true,
|
||||
)
|
||||
mapping.transformFindings(emptyMap())
|
||||
|
||||
mapping.transformFindings(emptyMap())
|
||||
assertThat(baselineFile.exists()).isFalse()
|
||||
}
|
||||
|
||||
assertThat(baselineFile.exists()).isFalse()
|
||||
}
|
||||
@Test
|
||||
fun `should not update an existing baseline file if option configured as false`() {
|
||||
val existing = Baseline.load(existingBaselineFile)
|
||||
val mapping = resultMapping(
|
||||
baselineFile = existingBaselineFile,
|
||||
createBaseline = false,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `should not update an existing baseline file if option configured as false`() {
|
||||
val existing = Baseline.load(existingBaselineFile)
|
||||
val mapping = resultMapping(
|
||||
baselineFile = existingBaselineFile,
|
||||
createBaseline = false,
|
||||
)
|
||||
mapping.transformFindings(findings)
|
||||
|
||||
mapping.transformFindings(findings)
|
||||
val changed = Baseline.load(existingBaselineFile)
|
||||
assertThat(existing).isEqualTo(changed)
|
||||
}
|
||||
|
||||
val changed = Baseline.load(existingBaselineFile)
|
||||
assertThat(existing).isEqualTo(changed)
|
||||
}
|
||||
@Test
|
||||
fun `should not update an existing baseline file if option is not configured`() {
|
||||
val existing = Baseline.load(existingBaselineFile)
|
||||
val mapping = resultMapping(
|
||||
baselineFile = existingBaselineFile,
|
||||
createBaseline = null,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `should not update an existing baseline file if option is not configured`() {
|
||||
val existing = Baseline.load(existingBaselineFile)
|
||||
val mapping = resultMapping(
|
||||
baselineFile = existingBaselineFile,
|
||||
createBaseline = null,
|
||||
)
|
||||
mapping.transformFindings(findings)
|
||||
|
||||
mapping.transformFindings(findings)
|
||||
val changed = Baseline.load(existingBaselineFile)
|
||||
assertThat(existing).isEqualTo(changed)
|
||||
}
|
||||
|
||||
val changed = Baseline.load(existingBaselineFile)
|
||||
assertThat(existing).isEqualTo(changed)
|
||||
}
|
||||
@Test
|
||||
fun `should not create a new baseline file if no file is configured`() {
|
||||
val mapping = resultMapping(
|
||||
baselineFile = null,
|
||||
createBaseline = false,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `should not create a new baseline file if no file is configured`() {
|
||||
val mapping = resultMapping(
|
||||
baselineFile = null,
|
||||
createBaseline = false,
|
||||
)
|
||||
mapping.transformFindings(findings)
|
||||
|
||||
mapping.transformFindings(findings)
|
||||
assertThat(baselineFile.exists()).isFalse()
|
||||
}
|
||||
|
||||
assertThat(baselineFile.exists()).isFalse()
|
||||
}
|
||||
@Test
|
||||
fun `should create a new baseline file if a file is configured`() {
|
||||
val mapping = resultMapping(
|
||||
baselineFile = baselineFile,
|
||||
createBaseline = true,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `should create a new baseline file if a file is configured`() {
|
||||
val mapping = resultMapping(
|
||||
baselineFile = baselineFile,
|
||||
createBaseline = true,
|
||||
)
|
||||
mapping.transformFindings(findings)
|
||||
|
||||
mapping.transformFindings(findings)
|
||||
assertThat(baselineFile.exists()).isTrue()
|
||||
}
|
||||
|
||||
assertThat(baselineFile.exists()).isTrue()
|
||||
}
|
||||
@Test
|
||||
fun `should update an existing baseline file if a file is configured`() {
|
||||
Files.copy(existingBaselineFile, baselineFile)
|
||||
val existing = Baseline.load(baselineFile)
|
||||
val mapping = resultMapping(
|
||||
baselineFile = baselineFile,
|
||||
createBaseline = true,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `should update an existing baseline file if a file is configured`() {
|
||||
Files.copy(existingBaselineFile, baselineFile)
|
||||
val existing = Baseline.load(baselineFile)
|
||||
val mapping = resultMapping(
|
||||
baselineFile = baselineFile,
|
||||
createBaseline = true,
|
||||
)
|
||||
mapping.transformFindings(findings)
|
||||
|
||||
mapping.transformFindings(findings)
|
||||
|
||||
val changed = Baseline.load(baselineFile)
|
||||
assertThat(existing).isNotEqualTo(changed)
|
||||
}
|
||||
val changed = Baseline.load(baselineFile)
|
||||
assertThat(existing).isNotEqualTo(changed)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,85 +13,80 @@ import io.gitlab.arturbosch.detekt.core.createProcessingSettings
|
||||
import io.gitlab.arturbosch.detekt.core.tooling.getDefaultConfiguration
|
||||
import io.gitlab.arturbosch.detekt.test.yamlConfigFromContent
|
||||
import org.assertj.core.api.Assertions.assertThatCode
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class SupportConfigValidationSpec {
|
||||
|
||||
@Nested
|
||||
inner class `support config validation` {
|
||||
private val testDir = createTempDirectoryForTest("detekt-sample")
|
||||
private val spec = createNullLoggingSpec {}
|
||||
|
||||
private val testDir = createTempDirectoryForTest("detekt-sample")
|
||||
private val spec = createNullLoggingSpec {}
|
||||
@Test
|
||||
fun `fails when unknown properties are found`() {
|
||||
val config = yamlConfigFromContent(
|
||||
"""
|
||||
# Properties of custom rule sets get excluded by default.
|
||||
sample-rule-set:
|
||||
TooManyFunctions:
|
||||
active: true
|
||||
|
||||
@Test
|
||||
fun `fails when unknown properties are found`() {
|
||||
val config = yamlConfigFromContent(
|
||||
"""
|
||||
# Properties of custom rule sets get excluded by default.
|
||||
sample-rule-set:
|
||||
TooManyFunctions:
|
||||
active: true
|
||||
|
||||
# This properties are unknown to detekt and must be excluded.
|
||||
my_additional_properties:
|
||||
magic_number: 7
|
||||
magic_string: 'Hello World'
|
||||
"""
|
||||
)
|
||||
createProcessingSettings(testDir, config).use {
|
||||
assertThatCode { checkConfiguration(it, spec.getDefaultConfiguration()) }
|
||||
.isInstanceOf(InvalidConfig::class.java)
|
||||
.hasMessageContaining("Run failed with 1 invalid config property.")
|
||||
.hasMessageContaining("my_additional_properties")
|
||||
}
|
||||
# This properties are unknown to detekt and must be excluded.
|
||||
my_additional_properties:
|
||||
magic_number: 7
|
||||
magic_string: 'Hello World'
|
||||
"""
|
||||
)
|
||||
createProcessingSettings(testDir, config).use {
|
||||
assertThatCode { checkConfiguration(it, spec.getDefaultConfiguration()) }
|
||||
.isInstanceOf(InvalidConfig::class.java)
|
||||
.hasMessageContaining("Run failed with 1 invalid config property.")
|
||||
.hasMessageContaining("my_additional_properties")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fails due to custom config validator want active to be booleans`() {
|
||||
val config = yamlConfigFromContent(
|
||||
"""
|
||||
# Properties of custom rule sets get excluded by default.
|
||||
sample-rule-set:
|
||||
TooManyFunctions:
|
||||
# This property is tested via the SampleConfigValidator
|
||||
active: 1 # should be true
|
||||
"""
|
||||
)
|
||||
createProcessingSettings(testDir, config).use {
|
||||
assertThatCode { checkConfiguration(it, spec.getDefaultConfiguration()) }
|
||||
.isInstanceOf(InvalidConfig::class.java)
|
||||
.hasMessageContaining("Run failed with 1 invalid config property.")
|
||||
}
|
||||
@Test
|
||||
fun `fails due to custom config validator want active to be booleans`() {
|
||||
val config = yamlConfigFromContent(
|
||||
"""
|
||||
# Properties of custom rule sets get excluded by default.
|
||||
sample-rule-set:
|
||||
TooManyFunctions:
|
||||
# This property is tested via the SampleConfigValidator
|
||||
active: 1 # should be true
|
||||
"""
|
||||
)
|
||||
createProcessingSettings(testDir, config).use {
|
||||
assertThatCode { checkConfiguration(it, spec.getDefaultConfiguration()) }
|
||||
.isInstanceOf(InvalidConfig::class.java)
|
||||
.hasMessageContaining("Run failed with 1 invalid config property.")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `passes with excluded new properties`() {
|
||||
val config = yamlConfigFromContent(
|
||||
"""
|
||||
config:
|
||||
validation: true
|
||||
# Additional properties can be useful when writing custom extensions.
|
||||
# However only properties defined in the default config are known to detekt.
|
||||
# All unknown properties are treated as errors if not excluded.
|
||||
excludes: 'my_additional_properties'
|
||||
@Test
|
||||
fun `passes with excluded new properties`() {
|
||||
val config = yamlConfigFromContent(
|
||||
"""
|
||||
config:
|
||||
validation: true
|
||||
# Additional properties can be useful when writing custom extensions.
|
||||
# However only properties defined in the default config are known to detekt.
|
||||
# All unknown properties are treated as errors if not excluded.
|
||||
excludes: 'my_additional_properties'
|
||||
|
||||
# Properties of custom rule sets get excluded by default.
|
||||
# If you want to validate them further, consider implementing a ConfigValidator.
|
||||
sample-rule-set:
|
||||
TooManyFunctions:
|
||||
active: true
|
||||
# Properties of custom rule sets get excluded by default.
|
||||
# If you want to validate them further, consider implementing a ConfigValidator.
|
||||
sample-rule-set:
|
||||
TooManyFunctions:
|
||||
active: true
|
||||
|
||||
# This properties are unknown to detekt and must be excluded.
|
||||
my_additional_properties:
|
||||
magic_number: 7
|
||||
magic_string: 'Hello World'
|
||||
"""
|
||||
)
|
||||
createProcessingSettings(testDir, config).use {
|
||||
assertThatCode { checkConfiguration(it, spec.getDefaultConfiguration()) }
|
||||
.doesNotThrowAnyException()
|
||||
}
|
||||
# This properties are unknown to detekt and must be excluded.
|
||||
my_additional_properties:
|
||||
magic_number: 7
|
||||
magic_string: 'Hello World'
|
||||
"""
|
||||
)
|
||||
createProcessingSettings(testDir, config).use {
|
||||
assertThatCode { checkConfiguration(it, spec.getDefaultConfiguration()) }
|
||||
.doesNotThrowAnyException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,54 +3,49 @@ package io.gitlab.arturbosch.detekt.core.config
|
||||
import io.gitlab.arturbosch.detekt.test.yamlConfig
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class CompositeConfigSpec {
|
||||
|
||||
@Nested
|
||||
inner class `both configs should be considered` {
|
||||
private val second = yamlConfig("composite-test.yml")
|
||||
private val first = yamlConfig("detekt.yml")
|
||||
private val compositeConfig = CompositeConfig(second, first)
|
||||
|
||||
private val second = yamlConfig("composite-test.yml")
|
||||
private val first = yamlConfig("detekt.yml")
|
||||
private val compositeConfig = CompositeConfig(second, first)
|
||||
@Test
|
||||
fun `should have style sub config with active false which is overridden in second config regardless of default value`() {
|
||||
val styleConfig = compositeConfig.subConfig("style").subConfig("WildcardImport")
|
||||
assertThat(styleConfig.valueOrDefault("active", true)).isEqualTo(false)
|
||||
assertThat(styleConfig.valueOrDefault("active", false)).isEqualTo(false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should have style sub config with active false which is overridden in second config regardless of default value`() {
|
||||
val styleConfig = compositeConfig.subConfig("style").subConfig("WildcardImport")
|
||||
assertThat(styleConfig.valueOrDefault("active", true)).isEqualTo(false)
|
||||
assertThat(styleConfig.valueOrDefault("active", false)).isEqualTo(false)
|
||||
}
|
||||
@Test
|
||||
fun `should have code smell sub config with LongMethod threshold 20 from _first_ config`() {
|
||||
val codeSmellConfig = compositeConfig.subConfig("code-smell").subConfig("LongMethod")
|
||||
assertThat(codeSmellConfig.valueOrDefault("threshold", -1)).isEqualTo(20)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should have code smell sub config with LongMethod threshold 20 from _first_ config`() {
|
||||
val codeSmellConfig = compositeConfig.subConfig("code-smell").subConfig("LongMethod")
|
||||
assertThat(codeSmellConfig.valueOrDefault("threshold", -1)).isEqualTo(20)
|
||||
}
|
||||
@Test
|
||||
fun `should use the default as both part configurations do not have the value`() {
|
||||
assertThat(compositeConfig.valueOrDefault("TEST", 42)).isEqualTo(42)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should use the default as both part configurations do not have the value`() {
|
||||
assertThat(compositeConfig.valueOrDefault("TEST", 42)).isEqualTo(42)
|
||||
}
|
||||
@Test
|
||||
fun `should return a string based on default value`() {
|
||||
val config = compositeConfig.subConfig("style").subConfig("MagicNumber")
|
||||
val value = config.valueOrDefault("ignoreNumbers", emptyList<String>())
|
||||
assertThat(value).isEqualTo(listOf("-1", "0", "1", "2", "100", "1000"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return a string based on default value`() {
|
||||
val config = compositeConfig.subConfig("style").subConfig("MagicNumber")
|
||||
val value = config.valueOrDefault("ignoreNumbers", emptyList<String>())
|
||||
assertThat(value).isEqualTo(listOf("-1", "0", "1", "2", "100", "1000"))
|
||||
}
|
||||
@Test
|
||||
fun `should fail with a meaningful exception when boolean property is invalid`() {
|
||||
val config = compositeConfig.subConfig("style").subConfig("LargeClass")
|
||||
|
||||
@Test
|
||||
fun `should fail with a meaningful exception when boolean property is invalid`() {
|
||||
val config = compositeConfig.subConfig("style").subConfig("LargeClass")
|
||||
val expectedErrorMessage = "Value \"truuu\" set for config parameter \"style > LargeClass > active\" " +
|
||||
"is not of required type Boolean"
|
||||
|
||||
val expectedErrorMessage = "Value \"truuu\" set for config parameter \"style > LargeClass > active\" " +
|
||||
"is not of required type Boolean"
|
||||
|
||||
assertThatThrownBy {
|
||||
config.valueOrDefault("active", true)
|
||||
}.isInstanceOf(IllegalStateException::class.java)
|
||||
.hasMessageContaining(expectedErrorMessage)
|
||||
}
|
||||
assertThatThrownBy {
|
||||
config.valueOrDefault("active", true)
|
||||
}.isInstanceOf(IllegalStateException::class.java)
|
||||
.hasMessageContaining(expectedErrorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,24 +2,19 @@ package io.gitlab.arturbosch.detekt.core.config
|
||||
|
||||
import io.gitlab.arturbosch.detekt.test.yamlConfig
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class DefaultConfigValidationSpec {
|
||||
|
||||
@Nested
|
||||
inner class `default configuration is valid` {
|
||||
private val baseline = yamlConfig("default-detekt-config.yml")
|
||||
|
||||
private val baseline = yamlConfig("default-detekt-config.yml")
|
||||
@Test
|
||||
fun `is valid comparing itself`() {
|
||||
assertThat(validateConfig(baseline, baseline)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `is valid comparing itself`() {
|
||||
assertThat(validateConfig(baseline, baseline)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not flag common known config sub sections`() {
|
||||
assertThat(validateConfig(yamlConfig("common_known_sections.yml"), baseline)).isEmpty()
|
||||
}
|
||||
@Test
|
||||
fun `does not flag common known config sub sections`() {
|
||||
assertThat(validateConfig(yamlConfig("common_known_sections.yml"), baseline)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,42 +16,121 @@ import org.junit.jupiter.params.provider.ValueSource
|
||||
|
||||
class ValidateConfigSpec {
|
||||
|
||||
private val baseline = yamlConfig("config_validation/baseline.yml")
|
||||
|
||||
@Test
|
||||
fun `passes for same config test`() {
|
||||
val result = validateConfig(baseline, baseline)
|
||||
assertThat(result).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `passes for properties which may appear on rules and rule sets but may be not present in default config`() {
|
||||
val result = validateConfig(
|
||||
yamlConfig("config_validation/default-excluded-properties.yml"),
|
||||
baseline
|
||||
)
|
||||
assertThat(result).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports different rule set name`() {
|
||||
val result = validateConfig(
|
||||
yamlConfig("config_validation/other-ruleset-name.yml"),
|
||||
baseline
|
||||
)
|
||||
assertThat(result).contains(propertyDoesNotExists("code-smell"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports different nested property names`() {
|
||||
val result = validateConfig(
|
||||
yamlConfig("config_validation/other-nested-property-names.yml"),
|
||||
baseline
|
||||
)
|
||||
assertThat(result).contains(
|
||||
propertyDoesNotExists("complexity>LongLongMethod"),
|
||||
propertyDoesNotExists("complexity>LongParameterList>enabled"),
|
||||
propertyDoesNotExists("complexity>LargeClass>howMany"),
|
||||
propertyDoesNotExists("complexity>InnerMap>InnerKey"),
|
||||
propertyDoesNotExists("complexity>InnerMap>Inner2>nestedActive")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports nested configuration expected`() {
|
||||
val result = validateConfig(
|
||||
yamlConfig("config_validation/no-nested-config.yml"),
|
||||
baseline
|
||||
)
|
||||
assertThat(result).contains(
|
||||
nestedConfigurationExpected("complexity"),
|
||||
nestedConfigurationExpected("style>WildcardImport")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports unexpected nested configs`() {
|
||||
// note that the baseline config is now the first argument
|
||||
val result = validateConfig(baseline, yamlConfig("config_validation/no-value.yml"))
|
||||
assertThat(result).contains(
|
||||
unexpectedNestedConfiguration("style"),
|
||||
unexpectedNestedConfiguration("comments")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns an error for an invalid config type`() {
|
||||
val invalidConfig = TestConfig()
|
||||
assertThatIllegalStateException().isThrownBy {
|
||||
validateConfig(invalidConfig, baseline)
|
||||
}.withMessageStartingWith("Unsupported config type for validation")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns an error for an invalid baseline`() {
|
||||
val invalidBaseline = TestConfig()
|
||||
assertThatIllegalArgumentException().isThrownBy {
|
||||
validateConfig(Config.empty, invalidBaseline)
|
||||
}.withMessageStartingWith("Only supported baseline config is the YamlConfig.")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns an error for an empty baseline`() {
|
||||
val invalidBaseline = Config.empty
|
||||
assertThatIllegalArgumentException().isThrownBy {
|
||||
validateConfig(Config.empty, invalidBaseline)
|
||||
}.withMessageStartingWith("Cannot validate configuration based on an empty baseline config.")
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `validate configuration file` {
|
||||
|
||||
private val baseline = yamlConfig("config_validation/baseline.yml")
|
||||
inner class `validate composite configurations` {
|
||||
|
||||
@Test
|
||||
fun `passes for same config test`() {
|
||||
val result = validateConfig(baseline, baseline)
|
||||
fun `passes for same left, right and baseline config`() {
|
||||
val result = validateConfig(CompositeConfig(baseline, baseline), baseline)
|
||||
assertThat(result).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `passes for properties which may appear on rules and rule sets but may be not present in default config`() {
|
||||
val result = validateConfig(
|
||||
yamlConfig("config_validation/default-excluded-properties.yml"),
|
||||
baseline
|
||||
)
|
||||
fun `passes for empty configs`() {
|
||||
val result = validateConfig(CompositeConfig(Config.empty, Config.empty), baseline)
|
||||
assertThat(result).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports different rule set name`() {
|
||||
fun `finds accumulated errors`() {
|
||||
val result = validateConfig(
|
||||
yamlConfig("config_validation/other-ruleset-name.yml"),
|
||||
CompositeConfig(
|
||||
yamlConfig("config_validation/other-nested-property-names.yml"),
|
||||
yamlConfig("config_validation/no-nested-config.yml")
|
||||
),
|
||||
baseline
|
||||
)
|
||||
assertThat(result).contains(propertyDoesNotExists("code-smell"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports different nested property names`() {
|
||||
val result = validateConfig(
|
||||
yamlConfig("config_validation/other-nested-property-names.yml"),
|
||||
baseline
|
||||
)
|
||||
assertThat(result).contains(
|
||||
nestedConfigurationExpected("complexity"),
|
||||
nestedConfigurationExpected("style>WildcardImport"),
|
||||
propertyDoesNotExists("complexity>LongLongMethod"),
|
||||
propertyDoesNotExists("complexity>LongParameterList>enabled"),
|
||||
propertyDoesNotExists("complexity>LargeClass>howMany"),
|
||||
@@ -59,173 +138,90 @@ class ValidateConfigSpec {
|
||||
propertyDoesNotExists("complexity>InnerMap>Inner2>nestedActive")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `configure additional exclude paths` {
|
||||
|
||||
private fun patterns(str: String) = CommaSeparatedPattern(str).mapToRegex()
|
||||
|
||||
@Test
|
||||
fun `reports nested configuration expected`() {
|
||||
fun `does not report any complexity properties`() {
|
||||
val result = validateConfig(
|
||||
yamlConfig("config_validation/no-nested-config.yml"),
|
||||
baseline
|
||||
yamlConfig("config_validation/other-nested-property-names.yml"),
|
||||
baseline,
|
||||
patterns("complexity")
|
||||
)
|
||||
assertThat(result).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report 'complexity_LargeClass_howMany'`() {
|
||||
val result = validateConfig(
|
||||
yamlConfig("config_validation/other-nested-property-names.yml"),
|
||||
baseline,
|
||||
patterns(".*>.*>howMany")
|
||||
)
|
||||
|
||||
assertThat(result).contains(
|
||||
nestedConfigurationExpected("complexity"),
|
||||
nestedConfigurationExpected("style>WildcardImport")
|
||||
propertyDoesNotExists("complexity>LongLongMethod"),
|
||||
propertyDoesNotExists("complexity>LongParameterList>enabled"),
|
||||
propertyDoesNotExists("complexity>InnerMap>InnerKey"),
|
||||
propertyDoesNotExists("complexity>InnerMap>Inner2>nestedActive")
|
||||
)
|
||||
|
||||
assertThat(result).doesNotContain(
|
||||
propertyDoesNotExists("complexity>LargeClass>howMany")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports unexpected nested configs`() {
|
||||
// note that the baseline config is now the first argument
|
||||
val result = validateConfig(baseline, yamlConfig("config_validation/no-value.yml"))
|
||||
@DisplayName("does not report .*>InnerMap")
|
||||
fun `does not report innerMap`() {
|
||||
val result = validateConfig(
|
||||
yamlConfig("config_validation/other-nested-property-names.yml"),
|
||||
baseline,
|
||||
patterns(".*>InnerMap")
|
||||
)
|
||||
|
||||
assertThat(result).contains(
|
||||
unexpectedNestedConfiguration("style"),
|
||||
unexpectedNestedConfiguration("comments")
|
||||
propertyDoesNotExists("complexity>LargeClass>howMany"),
|
||||
propertyDoesNotExists("complexity>LongLongMethod"),
|
||||
propertyDoesNotExists("complexity>LongParameterList>enabled")
|
||||
)
|
||||
|
||||
assertThat(result).doesNotContain(
|
||||
propertyDoesNotExists("complexity>InnerMap>InnerKey"),
|
||||
propertyDoesNotExists("complexity>InnerMap>Inner2>nestedActive")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns an error for an invalid config type`() {
|
||||
val invalidConfig = TestConfig()
|
||||
assertThatIllegalStateException().isThrownBy {
|
||||
validateConfig(invalidConfig, baseline)
|
||||
}.withMessageStartingWith("Unsupported config type for validation")
|
||||
}
|
||||
@Nested
|
||||
inner class `deprecated configuration option` {
|
||||
|
||||
@Test
|
||||
fun `returns an error for an invalid baseline`() {
|
||||
val invalidBaseline = TestConfig()
|
||||
assertThatIllegalArgumentException().isThrownBy {
|
||||
validateConfig(Config.empty, invalidBaseline)
|
||||
}.withMessageStartingWith("Only supported baseline config is the YamlConfig.")
|
||||
}
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = [true, false])
|
||||
fun `reports a deprecated property as a warning`(warningsAsErrors: Boolean) {
|
||||
val config = yamlConfigFromContent(
|
||||
"""
|
||||
config:
|
||||
warningsAsErrors: $warningsAsErrors
|
||||
naming:
|
||||
FunctionParameterNaming:
|
||||
ignoreOverriddenFunctions: ''
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `returns an error for an empty baseline`() {
|
||||
val invalidBaseline = Config.empty
|
||||
assertThatIllegalArgumentException().isThrownBy {
|
||||
validateConfig(Config.empty, invalidBaseline)
|
||||
}.withMessageStartingWith("Cannot validate configuration based on an empty baseline config.")
|
||||
}
|
||||
val result = validateConfig(config, config)
|
||||
|
||||
@Nested
|
||||
inner class `validate composite configurations` {
|
||||
|
||||
@Test
|
||||
fun `passes for same left, right and baseline config`() {
|
||||
val result = validateConfig(CompositeConfig(baseline, baseline), baseline)
|
||||
assertThat(result).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `passes for empty configs`() {
|
||||
val result = validateConfig(CompositeConfig(Config.empty, Config.empty), baseline)
|
||||
assertThat(result).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `finds accumulated errors`() {
|
||||
val result = validateConfig(
|
||||
CompositeConfig(
|
||||
yamlConfig("config_validation/other-nested-property-names.yml"),
|
||||
yamlConfig("config_validation/no-nested-config.yml")
|
||||
),
|
||||
baseline
|
||||
assertThat(result).contains(
|
||||
propertyIsDeprecated(
|
||||
"naming>FunctionParameterNaming>ignoreOverriddenFunctions",
|
||||
"Use `ignoreOverridden` instead",
|
||||
reportAsError = warningsAsErrors
|
||||
)
|
||||
|
||||
assertThat(result).contains(
|
||||
nestedConfigurationExpected("complexity"),
|
||||
nestedConfigurationExpected("style>WildcardImport"),
|
||||
propertyDoesNotExists("complexity>LongLongMethod"),
|
||||
propertyDoesNotExists("complexity>LongParameterList>enabled"),
|
||||
propertyDoesNotExists("complexity>LargeClass>howMany"),
|
||||
propertyDoesNotExists("complexity>InnerMap>InnerKey"),
|
||||
propertyDoesNotExists("complexity>InnerMap>Inner2>nestedActive")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `configure additional exclude paths` {
|
||||
|
||||
fun patterns(str: String) = CommaSeparatedPattern(str).mapToRegex()
|
||||
|
||||
@Test
|
||||
fun `does not report any complexity properties`() {
|
||||
val result = validateConfig(
|
||||
yamlConfig("config_validation/other-nested-property-names.yml"),
|
||||
baseline,
|
||||
patterns("complexity")
|
||||
)
|
||||
assertThat(result).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report 'complexity_LargeClass_howMany'`() {
|
||||
val result = validateConfig(
|
||||
yamlConfig("config_validation/other-nested-property-names.yml"),
|
||||
baseline,
|
||||
patterns(".*>.*>howMany")
|
||||
)
|
||||
|
||||
assertThat(result).contains(
|
||||
propertyDoesNotExists("complexity>LongLongMethod"),
|
||||
propertyDoesNotExists("complexity>LongParameterList>enabled"),
|
||||
propertyDoesNotExists("complexity>InnerMap>InnerKey"),
|
||||
propertyDoesNotExists("complexity>InnerMap>Inner2>nestedActive")
|
||||
)
|
||||
|
||||
assertThat(result).doesNotContain(
|
||||
propertyDoesNotExists("complexity>LargeClass>howMany")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("does not report .*>InnerMap")
|
||||
fun `does not report innerMap`() {
|
||||
val result = validateConfig(
|
||||
yamlConfig("config_validation/other-nested-property-names.yml"),
|
||||
baseline,
|
||||
patterns(".*>InnerMap")
|
||||
)
|
||||
|
||||
assertThat(result).contains(
|
||||
propertyDoesNotExists("complexity>LargeClass>howMany"),
|
||||
propertyDoesNotExists("complexity>LongLongMethod"),
|
||||
propertyDoesNotExists("complexity>LongParameterList>enabled")
|
||||
)
|
||||
|
||||
assertThat(result).doesNotContain(
|
||||
propertyDoesNotExists("complexity>InnerMap>InnerKey"),
|
||||
propertyDoesNotExists("complexity>InnerMap>Inner2>nestedActive")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `deprecated configuration option` {
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = [true, false])
|
||||
fun `reports a deprecated property as a warning`(warningsAsErrors: Boolean) {
|
||||
val config = yamlConfigFromContent(
|
||||
"""
|
||||
config:
|
||||
warningsAsErrors: $warningsAsErrors
|
||||
naming:
|
||||
FunctionParameterNaming:
|
||||
ignoreOverriddenFunctions: ''
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val result = validateConfig(config, config)
|
||||
|
||||
assertThat(result).contains(
|
||||
propertyIsDeprecated(
|
||||
"naming>FunctionParameterNaming>ignoreOverriddenFunctions",
|
||||
"Use `ignoreOverridden` instead",
|
||||
reportAsError = warningsAsErrors
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,102 +20,98 @@ import java.util.function.Predicate
|
||||
class OutputReportsSpec {
|
||||
|
||||
@Nested
|
||||
inner class `reports` {
|
||||
inner class `arguments for spec` {
|
||||
|
||||
@Nested
|
||||
inner class `arguments for spec` {
|
||||
private val reportUnderTest = TestOutputReport::class.java.simpleName
|
||||
private val reports = ReportsSpecBuilder().apply {
|
||||
report { "xml" to Paths.get("/tmp/path1") }
|
||||
report { "txt" to Paths.get("/tmp/path2") }
|
||||
report { reportUnderTest to Paths.get("/tmp/path3") }
|
||||
report { "html" to Paths.get("D:_Gradle\\xxx\\xxx\\build\\reports\\detekt\\detekt.html") }
|
||||
}.build().reports.toList()
|
||||
|
||||
private val reportUnderTest = TestOutputReport::class.java.simpleName
|
||||
private val reports = ReportsSpecBuilder().apply {
|
||||
report { "xml" to Paths.get("/tmp/path1") }
|
||||
report { "txt" to Paths.get("/tmp/path2") }
|
||||
report { reportUnderTest to Paths.get("/tmp/path3") }
|
||||
report { "html" to Paths.get("D:_Gradle\\xxx\\xxx\\build\\reports\\detekt\\detekt.html") }
|
||||
}.build().reports.toList()
|
||||
@Test
|
||||
fun `should parse multiple report entries`() {
|
||||
assertThat(reports).hasSize(4)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should parse multiple report entries`() {
|
||||
assertThat(reports).hasSize(4)
|
||||
}
|
||||
@Test
|
||||
fun `it should properly parse XML report entry`() {
|
||||
val xmlReport = reports[0]
|
||||
assertThat(xmlReport.type).isEqualTo(defaultReportMapping(XmlOutputReport::class.java.simpleName))
|
||||
assertThat(xmlReport.path).isEqualTo(Paths.get("/tmp/path1"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `it should properly parse XML report entry`() {
|
||||
val xmlReport = reports[0]
|
||||
assertThat(xmlReport.type).isEqualTo(defaultReportMapping(XmlOutputReport::class.java.simpleName))
|
||||
assertThat(xmlReport.path).isEqualTo(Paths.get("/tmp/path1"))
|
||||
}
|
||||
@Test
|
||||
fun `it should properly parse TXT report entry`() {
|
||||
val txtRepot = reports[1]
|
||||
assertThat(txtRepot.type).isEqualTo(defaultReportMapping(TxtOutputReport::class.java.simpleName))
|
||||
assertThat(txtRepot.path).isEqualTo(Paths.get("/tmp/path2"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `it should properly parse TXT report entry`() {
|
||||
val txtRepot = reports[1]
|
||||
assertThat(txtRepot.type).isEqualTo(defaultReportMapping(TxtOutputReport::class.java.simpleName))
|
||||
assertThat(txtRepot.path).isEqualTo(Paths.get("/tmp/path2"))
|
||||
}
|
||||
@Test
|
||||
fun `it should properly parse custom report entry`() {
|
||||
val customReport = reports[2]
|
||||
assertThat(customReport.type).isEqualTo(reportUnderTest)
|
||||
assertThat(defaultReportMapping(customReport.type)).isEqualTo(reportUnderTest)
|
||||
assertThat(customReport.path).isEqualTo(Paths.get("/tmp/path3"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `it should properly parse custom report entry`() {
|
||||
val customReport = reports[2]
|
||||
assertThat(customReport.type).isEqualTo(reportUnderTest)
|
||||
assertThat(defaultReportMapping(customReport.type)).isEqualTo(reportUnderTest)
|
||||
assertThat(customReport.path).isEqualTo(Paths.get("/tmp/path3"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `it should properly parse HTML report entry`() {
|
||||
val htmlReport = reports[3]
|
||||
assertThat(htmlReport.type).isEqualTo(defaultReportMapping(HtmlOutputReport::class.java.simpleName))
|
||||
assertThat(htmlReport.path).isEqualTo(
|
||||
Paths.get("D:_Gradle\\xxx\\xxx\\build\\reports\\detekt\\detekt.html")
|
||||
)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `default report ids` {
|
||||
|
||||
private val extensions = createProcessingSettings().use { OutputReportLocator(it).load() }
|
||||
private val extensionsIds = extensions.mapTo(HashSet()) { defaultReportMapping(it.id) }
|
||||
|
||||
@Test
|
||||
fun `should be able to convert to output reports`() {
|
||||
assertThat(reports).allMatch { it.type in extensionsIds }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should recognize custom output format`() {
|
||||
assertThat(reports).haveExactly(
|
||||
1,
|
||||
Condition(
|
||||
Predicate { it.type == reportUnderTest },
|
||||
"Corresponds exactly to the test output report."
|
||||
)
|
||||
)
|
||||
|
||||
assertThat(extensions).haveExactly(
|
||||
1,
|
||||
Condition(
|
||||
Predicate { it is TestOutputReport && it.ending == "yml" },
|
||||
"Is exactly the test output report."
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun `it should properly parse HTML report entry`() {
|
||||
val htmlReport = reports[3]
|
||||
assertThat(htmlReport.type).isEqualTo(defaultReportMapping(HtmlOutputReport::class.java.simpleName))
|
||||
assertThat(htmlReport.path).isEqualTo(
|
||||
Paths.get("D:_Gradle\\xxx\\xxx\\build\\reports\\detekt\\detekt.html")
|
||||
)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `empty reports` {
|
||||
inner class `default report ids` {
|
||||
|
||||
private val extensions = createProcessingSettings().use { OutputReportLocator(it).load() }
|
||||
private val extensionsIds = extensions.mapTo(HashSet()) { defaultReportMapping(it.id) }
|
||||
|
||||
@Test
|
||||
fun `yields empty extension list`() {
|
||||
val spec = createNullLoggingSpec {
|
||||
config {
|
||||
configPaths = listOf(resourceAsPath("/reporting/disabled-reports.yml"))
|
||||
}
|
||||
}
|
||||
|
||||
val extensions = spec.withSettings { ConsoleReportLocator(this).load() }
|
||||
|
||||
assertThat(extensions).isEmpty()
|
||||
fun `should be able to convert to output reports`() {
|
||||
assertThat(reports).allMatch { it.type in extensionsIds }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should recognize custom output format`() {
|
||||
assertThat(reports).haveExactly(
|
||||
1,
|
||||
Condition(
|
||||
Predicate { it.type == reportUnderTest },
|
||||
"Corresponds exactly to the test output report."
|
||||
)
|
||||
)
|
||||
|
||||
assertThat(extensions).haveExactly(
|
||||
1,
|
||||
Condition(
|
||||
Predicate { it is TestOutputReport && it.ending == "yml" },
|
||||
"Is exactly the test output report."
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `empty reports` {
|
||||
|
||||
@Test
|
||||
fun `yields empty extension list`() {
|
||||
val spec = createNullLoggingSpec {
|
||||
config {
|
||||
configPaths = listOf(resourceAsPath("/reporting/disabled-reports.yml"))
|
||||
}
|
||||
}
|
||||
|
||||
val extensions = spec.withSettings { ConsoleReportLocator(this).load() }
|
||||
|
||||
assertThat(extensions).isEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,33 +11,24 @@ import io.gitlab.arturbosch.detekt.api.Detektion
|
||||
import io.gitlab.arturbosch.detekt.core.DetektResult
|
||||
import io.gitlab.arturbosch.detekt.test.createFinding
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class ComplexityReportSpec {
|
||||
|
||||
@Nested
|
||||
inner class `complexity report` {
|
||||
@Test
|
||||
fun `successfully generates a complexity report`() {
|
||||
val report = ComplexityReport()
|
||||
val expectedContent = readResourceContent("/reporting/complexity-report.txt")
|
||||
val detektion = createDetektion()
|
||||
addData(detektion)
|
||||
assertThat(report.render(detektion)).isEqualTo(expectedContent)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `several complexity metrics` {
|
||||
|
||||
@Test
|
||||
fun `successfully generates a complexity report`() {
|
||||
val report = ComplexityReport()
|
||||
val expectedContent = readResourceContent("/reporting/complexity-report.txt")
|
||||
val detektion = createDetektion()
|
||||
addData(detektion)
|
||||
assertThat(report.render(detektion)).isEqualTo(expectedContent)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns null for missing complexity metrics in report`() {
|
||||
val report = ComplexityReport()
|
||||
val detektion = createDetektion()
|
||||
assertThat(report.render(detektion)).isNull()
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun `returns null for missing complexity metrics in report`() {
|
||||
val report = ComplexityReport()
|
||||
val detektion = createDetektion()
|
||||
assertThat(report.render(detektion)).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,53 +16,49 @@ class FileBasedFindingsReportSpec {
|
||||
private val subject = createFileBasedFindingsReport()
|
||||
|
||||
@Nested
|
||||
inner class `findings report` {
|
||||
|
||||
@Nested
|
||||
inner class `reports the debt per file and rule set with the overall debt` {
|
||||
|
||||
@Test
|
||||
fun `has the reference content`() {
|
||||
val expectedContent = readResourceContent("/reporting/grouped-findings-report.txt")
|
||||
val detektion = object : TestDetektion() {
|
||||
override val findings: Map<String, List<Finding>> = mapOf(
|
||||
"Ruleset1" to listOf(
|
||||
createFinding(fileName = "File1.kt"),
|
||||
createFinding(fileName = "File2.kt")
|
||||
),
|
||||
"EmptyRuleset" to emptyList(),
|
||||
"Ruleset2" to listOf(createFinding(fileName = "File1.kt"))
|
||||
)
|
||||
}
|
||||
|
||||
val output = subject.render(detektion)?.decolorized()
|
||||
|
||||
assertThat(output).isEqualTo(expectedContent)
|
||||
}
|
||||
}
|
||||
inner class `reports the debt per file and rule set with the overall debt` {
|
||||
|
||||
@Test
|
||||
fun `reports no findings`() {
|
||||
val detektion = TestDetektion()
|
||||
assertThat(subject.render(detektion)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports no findings when no rule set contains smells`() {
|
||||
fun `has the reference content`() {
|
||||
val expectedContent = readResourceContent("/reporting/grouped-findings-report.txt")
|
||||
val detektion = object : TestDetektion() {
|
||||
override val findings: Map<String, List<Finding>> = mapOf(
|
||||
"EmptySmells" to emptyList()
|
||||
"Ruleset1" to listOf(
|
||||
createFinding(fileName = "File1.kt"),
|
||||
createFinding(fileName = "File2.kt")
|
||||
),
|
||||
"EmptyRuleset" to emptyList(),
|
||||
"Ruleset2" to listOf(createFinding(fileName = "File1.kt"))
|
||||
)
|
||||
}
|
||||
assertThat(subject.render(detektion)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not add auto corrected issues to report`() {
|
||||
val report = FileBasedFindingsReport()
|
||||
AutoCorrectableIssueAssert.isReportNull(report)
|
||||
val output = subject.render(detektion)?.decolorized()
|
||||
|
||||
assertThat(output).isEqualTo(expectedContent)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports no findings`() {
|
||||
val detektion = TestDetektion()
|
||||
assertThat(subject.render(detektion)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports no findings when no rule set contains smells`() {
|
||||
val detektion = object : TestDetektion() {
|
||||
override val findings: Map<String, List<Finding>> = mapOf(
|
||||
"EmptySmells" to emptyList()
|
||||
)
|
||||
}
|
||||
assertThat(subject.render(detektion)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not add auto corrected issues to report`() {
|
||||
val report = FileBasedFindingsReport()
|
||||
AutoCorrectableIssueAssert.isReportNull(report)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createFileBasedFindingsReport(): FileBasedFindingsReport {
|
||||
|
||||
@@ -19,75 +19,71 @@ class FindingsReportSpec {
|
||||
private val subject = createFindingsReport()
|
||||
|
||||
@Nested
|
||||
inner class `findings report` {
|
||||
inner class `reports the debt per rule set and the overall debt` {
|
||||
private val expectedContent = readResourceContent("/reporting/findings-report.txt")
|
||||
val detektion = object : TestDetektion() {
|
||||
override val findings: Map<String, List<Finding>> = mapOf(
|
||||
"Ruleset1" to listOf(createFinding(), createFinding()),
|
||||
"EmptyRuleset" to emptyList(),
|
||||
"Ruleset2" to listOf(createFinding())
|
||||
)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `reports the debt per rule set and the overall debt` {
|
||||
private val expectedContent = readResourceContent("/reporting/findings-report.txt")
|
||||
val detektion = object : TestDetektion() {
|
||||
override val findings: Map<String, List<Finding>> = mapOf(
|
||||
"Ruleset1" to listOf(createFinding(), createFinding()),
|
||||
"EmptyRuleset" to emptyList(),
|
||||
"Ruleset2" to listOf(createFinding())
|
||||
)
|
||||
}
|
||||
var output: String? = null
|
||||
|
||||
var output: String? = null
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
output = subject.render(detektion)?.decolorized()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `has the reference content`() {
|
||||
assertThat(output).isEqualTo(expectedContent)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does contain the rule set id of rule sets with findings`() {
|
||||
assertThat(output).contains("TestSmell")
|
||||
}
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
output = subject.render(detektion)?.decolorized()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports no findings`() {
|
||||
val detektion = TestDetektion()
|
||||
assertThat(subject.render(detektion)).isNull()
|
||||
fun `has the reference content`() {
|
||||
assertThat(output).isEqualTo(expectedContent)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports no findings with rule set containing no smells`() {
|
||||
val detektion = object : TestDetektion() {
|
||||
override val findings: Map<String, List<Finding>> = mapOf(
|
||||
"Ruleset" to emptyList()
|
||||
)
|
||||
}
|
||||
assertThat(subject.render(detektion)).isNull()
|
||||
fun `does contain the rule set id of rule sets with findings`() {
|
||||
assertThat(output).contains("TestSmell")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not add auto corrected issues to report`() {
|
||||
val report = FindingsReport()
|
||||
AutoCorrectableIssueAssert.isReportNull(report)
|
||||
}
|
||||
@Test
|
||||
fun `reports no findings`() {
|
||||
val detektion = TestDetektion()
|
||||
assertThat(subject.render(detektion)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `truncates long message`() {
|
||||
val expectedContent = readResourceContent("/reporting/long-messages-report.txt")
|
||||
val longMessage = "This is just a long message that should be truncated after a given " +
|
||||
"threshold is reached."
|
||||
val multilineMessage = "A multiline\n\r\tmessage.\t "
|
||||
val detektion = object : TestDetektion() {
|
||||
override val findings: Map<String, List<Finding>> = mapOf(
|
||||
"Ruleset" to listOf(
|
||||
createFinding(createIssue("LongRule"), createEntity("File.kt"), longMessage),
|
||||
createFinding(createIssue("MultilineRule"), createEntity("File.kt"), multilineMessage),
|
||||
),
|
||||
)
|
||||
}
|
||||
assertThat(subject.render(detektion)?.decolorized()).isEqualTo(expectedContent)
|
||||
@Test
|
||||
fun `reports no findings with rule set containing no smells`() {
|
||||
val detektion = object : TestDetektion() {
|
||||
override val findings: Map<String, List<Finding>> = mapOf(
|
||||
"Ruleset" to emptyList()
|
||||
)
|
||||
}
|
||||
assertThat(subject.render(detektion)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not add auto corrected issues to report`() {
|
||||
val report = FindingsReport()
|
||||
AutoCorrectableIssueAssert.isReportNull(report)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `truncates long message`() {
|
||||
val expectedContent = readResourceContent("/reporting/long-messages-report.txt")
|
||||
val longMessage = "This is just a long message that should be truncated after a given " +
|
||||
"threshold is reached."
|
||||
val multilineMessage = "A multiline\n\r\tmessage.\t "
|
||||
val detektion = object : TestDetektion() {
|
||||
override val findings: Map<String, List<Finding>> = mapOf(
|
||||
"Ruleset" to listOf(
|
||||
createFinding(createIssue("LongRule"), createEntity("File.kt"), longMessage),
|
||||
createFinding(createIssue("MultilineRule"), createEntity("File.kt"), multilineMessage),
|
||||
),
|
||||
)
|
||||
}
|
||||
assertThat(subject.render(detektion)?.decolorized()).isEqualTo(expectedContent)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,49 +7,45 @@ import io.gitlab.arturbosch.detekt.core.reporting.AutoCorrectableIssueAssert
|
||||
import io.gitlab.arturbosch.detekt.test.TestDetektion
|
||||
import io.gitlab.arturbosch.detekt.test.createFinding
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class LiteFindingsReportSpec {
|
||||
|
||||
private val subject = createFindingsReport()
|
||||
|
||||
@Nested
|
||||
inner class `findings report` {
|
||||
@Test
|
||||
fun `reports non-empty findings`() {
|
||||
assertThat(
|
||||
subject
|
||||
.render(
|
||||
TestDetektion(
|
||||
createFinding("SpacingBetweenPackageAndImports"),
|
||||
createFinding("UnnecessarySafeCall")
|
||||
)
|
||||
@Test
|
||||
fun `reports non-empty findings`() {
|
||||
assertThat(
|
||||
subject
|
||||
.render(
|
||||
TestDetektion(
|
||||
createFinding("SpacingBetweenPackageAndImports"),
|
||||
createFinding("UnnecessarySafeCall")
|
||||
)
|
||||
).isEqualTo(readResourceContent("/reporting/lite-findings-report.txt"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports no findings`() {
|
||||
val detektion = TestDetektion()
|
||||
assertThat(subject.render(detektion)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports no findings with rule set containing no smells`() {
|
||||
val detektion = object : TestDetektion() {
|
||||
override val findings: Map<String, List<Finding>> = mapOf(
|
||||
"Ruleset" to emptyList()
|
||||
)
|
||||
}
|
||||
assertThat(subject.render(detektion)).isNull()
|
||||
}
|
||||
).isEqualTo(readResourceContent("/reporting/lite-findings-report.txt"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not add auto corrected issues to report`() {
|
||||
val report = LiteFindingsReport()
|
||||
AutoCorrectableIssueAssert.isReportNull(report)
|
||||
@Test
|
||||
fun `reports no findings`() {
|
||||
val detektion = TestDetektion()
|
||||
assertThat(subject.render(detektion)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports no findings with rule set containing no smells`() {
|
||||
val detektion = object : TestDetektion() {
|
||||
override val findings: Map<String, List<Finding>> = mapOf(
|
||||
"Ruleset" to emptyList()
|
||||
)
|
||||
}
|
||||
assertThat(subject.render(detektion)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not add auto corrected issues to report`() {
|
||||
val report = LiteFindingsReport()
|
||||
AutoCorrectableIssueAssert.isReportNull(report)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,28 +4,23 @@ import io.gitlab.arturbosch.detekt.api.internal.SimpleNotification
|
||||
import io.gitlab.arturbosch.detekt.core.NL
|
||||
import io.gitlab.arturbosch.detekt.test.TestDetektion
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class NotificationReportSpec {
|
||||
|
||||
private val subject = NotificationReport()
|
||||
|
||||
@Nested
|
||||
inner class `notification report` {
|
||||
|
||||
@Test
|
||||
fun `reports two notifications`() {
|
||||
val detektion = object : TestDetektion() {
|
||||
override val notifications = listOf(SimpleNotification("test"), SimpleNotification("test"))
|
||||
}
|
||||
assertThat(subject.render(detektion)).isEqualTo("test${NL}test")
|
||||
@Test
|
||||
fun `reports two notifications`() {
|
||||
val detektion = object : TestDetektion() {
|
||||
override val notifications = listOf(SimpleNotification("test"), SimpleNotification("test"))
|
||||
}
|
||||
assertThat(subject.render(detektion)).isEqualTo("test${NL}test")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports no findings`() {
|
||||
val detektion = TestDetektion()
|
||||
assertThat(subject.render(detektion)).isNull()
|
||||
}
|
||||
@Test
|
||||
fun `reports no findings`() {
|
||||
val detektion = TestDetektion()
|
||||
assertThat(subject.render(detektion)).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,31 +3,26 @@ package io.gitlab.arturbosch.detekt.core.reporting.console
|
||||
import io.gitlab.arturbosch.detekt.api.ProjectMetric
|
||||
import io.gitlab.arturbosch.detekt.test.TestDetektion
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class ProjectStatisticsReportSpec {
|
||||
|
||||
private val subject = ProjectStatisticsReport()
|
||||
|
||||
@Nested
|
||||
inner class `project statistics` {
|
||||
|
||||
@Test
|
||||
fun `reports the project statistics`() {
|
||||
val expected = "Project Statistics:\n\t- M2: 2\n\t- M1: 1\n"
|
||||
val detektion = object : TestDetektion() {
|
||||
override val metrics: Collection<ProjectMetric> = listOf(
|
||||
ProjectMetric("M1", 1, priority = 1),
|
||||
ProjectMetric("M2", 2, priority = 2)
|
||||
)
|
||||
}
|
||||
assertThat(subject.render(detektion)).isEqualTo(expected)
|
||||
@Test
|
||||
fun `reports the project statistics`() {
|
||||
val expected = "Project Statistics:\n\t- M2: 2\n\t- M1: 1\n"
|
||||
val detektion = object : TestDetektion() {
|
||||
override val metrics: Collection<ProjectMetric> = listOf(
|
||||
ProjectMetric("M1", 1, priority = 1),
|
||||
ProjectMetric("M2", 2, priority = 2)
|
||||
)
|
||||
}
|
||||
assertThat(subject.render(detektion)).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report anything for zero metrics`() {
|
||||
assertThat(subject.render(TestDetektion())).isNull()
|
||||
}
|
||||
@Test
|
||||
fun `does not report anything for zero metrics`() {
|
||||
assertThat(subject.render(TestDetektion())).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,61 +16,52 @@ import io.gitlab.arturbosch.detekt.test.loadRuleSet
|
||||
import io.gitlab.arturbosch.detekt.test.yamlConfig
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class MultiRuleSpec {
|
||||
|
||||
@Nested
|
||||
inner class `a multi rule` {
|
||||
private val file = compileForTest(resourceAsPath("/cases/Default.kt"))
|
||||
|
||||
private val file = compileForTest(resourceAsPath("/cases/Default.kt"))
|
||||
@Test
|
||||
fun `should not run any rules if rule set defines the filter`() {
|
||||
val config = yamlConfig("/pathFilters/multi-rule-with-excludes-on-ruleset.yml")
|
||||
assertThat(config.subConfig("TestMultiRule").shouldAnalyzeFile(file)).isFalse()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `runs once on a KtFile for every rules and respects configured path filters` {
|
||||
@Test
|
||||
fun `should not run any rules if rule set defines the filter with string`() {
|
||||
val config = yamlConfig("/pathFilters/multi-rule-with-excludes-on-ruleset-string.yml")
|
||||
assertThat(config.subConfig("TestMultiRule").shouldAnalyzeFile(file)).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not run any rules if rule set defines the filter`() {
|
||||
val config = yamlConfig("/pathFilters/multi-rule-with-excludes-on-ruleset.yml")
|
||||
assertThat(config.subConfig("TestMultiRule").shouldAnalyzeFile(file)).isFalse()
|
||||
}
|
||||
@Test
|
||||
fun `should only run one rule as the other is filtered`() {
|
||||
val config = yamlConfig("/pathFilters/multi-rule-with-one-exclude.yml")
|
||||
assertThat(loadRuleSet<MultiRuleProvider>(config).visitFile(file)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not run any rules if rule set defines the filter with string`() {
|
||||
val config = yamlConfig("/pathFilters/multi-rule-with-excludes-on-ruleset-string.yml")
|
||||
assertThat(config.subConfig("TestMultiRule").shouldAnalyzeFile(file)).isFalse()
|
||||
}
|
||||
@Test
|
||||
fun `should only run one rule as the other is filtered with string`() {
|
||||
val config = yamlConfig("/pathFilters/multi-rule-with-one-exclude-string.yml")
|
||||
assertThat(loadRuleSet<MultiRuleProvider>(config).visitFile(file)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should only run one rule as the other is filtered`() {
|
||||
val config = yamlConfig("/pathFilters/multi-rule-with-one-exclude.yml")
|
||||
assertThat(loadRuleSet<MultiRuleProvider>(config).visitFile(file)).hasSize(1)
|
||||
}
|
||||
@Test
|
||||
fun `should run both when no filter is applied`() {
|
||||
val config = yamlConfig("/pathFilters/multi-rule-without-excludes.yml")
|
||||
assertThat(loadRuleSet<MultiRuleProvider>(config).visitFile(file)).hasSize(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should only run one rule as the other is filtered with string`() {
|
||||
val config = yamlConfig("/pathFilters/multi-rule-with-one-exclude-string.yml")
|
||||
assertThat(loadRuleSet<MultiRuleProvider>(config).visitFile(file)).hasSize(1)
|
||||
}
|
||||
@Test
|
||||
fun `should run none when both rules are filtered`() {
|
||||
val config = yamlConfig("/pathFilters/multi-rule-with-excludes.yml")
|
||||
assertThat(loadRuleSet<MultiRuleProvider>(config).visitFile(file)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should run both when no filter is applied`() {
|
||||
val config = yamlConfig("/pathFilters/multi-rule-without-excludes.yml")
|
||||
assertThat(loadRuleSet<MultiRuleProvider>(config).visitFile(file)).hasSize(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should run none when both rules are filtered`() {
|
||||
val config = yamlConfig("/pathFilters/multi-rule-with-excludes.yml")
|
||||
assertThat(loadRuleSet<MultiRuleProvider>(config).visitFile(file)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should run none when both rules are filtered with string`() {
|
||||
val config = yamlConfig("/pathFilters/multi-rule-with-excludes-string.yml")
|
||||
assertThat(loadRuleSet<MultiRuleProvider>(config).visitFile(file)).isEmpty()
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun `should run none when both rules are filtered with string`() {
|
||||
val config = yamlConfig("/pathFilters/multi-rule-with-excludes-string.yml")
|
||||
assertThat(loadRuleSet<MultiRuleProvider>(config).visitFile(file)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,36 +5,31 @@ import io.gitlab.arturbosch.detekt.core.createNullLoggingSpec
|
||||
import io.gitlab.arturbosch.detekt.core.tooling.withSettings
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.fail
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.reflections.Reflections
|
||||
import java.lang.reflect.Modifier
|
||||
|
||||
class RuleSetLocatorSpec {
|
||||
|
||||
@Nested
|
||||
inner class `locating RuleSetProvider's` {
|
||||
@Test
|
||||
fun `contains all RuleSetProviders`() {
|
||||
val providers = createNullLoggingSpec().withSettings { RuleSetLocator(this).load() }
|
||||
val providerClasses = getProviderClasses()
|
||||
|
||||
@Test
|
||||
fun `contains all RuleSetProviders`() {
|
||||
val providers = createNullLoggingSpec().withSettings { RuleSetLocator(this).load() }
|
||||
val providerClasses = getProviderClasses()
|
||||
assertThat(providerClasses).isNotEmpty
|
||||
providerClasses
|
||||
.filter { clazz -> providers.none { it.javaClass == clazz } }
|
||||
.forEach { fail("$it rule set is not loaded by the RuleSetLocator") }
|
||||
}
|
||||
|
||||
assertThat(providerClasses).isNotEmpty
|
||||
providerClasses
|
||||
.filter { clazz -> providers.none { it.javaClass == clazz } }
|
||||
.forEach { fail("$it rule set is not loaded by the RuleSetLocator") }
|
||||
}
|
||||
@Test
|
||||
fun `does not load any default rule set provider when opt out`() {
|
||||
val providers = createNullLoggingSpec { extensions { disableDefaultRuleSets = true } }
|
||||
.withSettings { RuleSetLocator(this).load() }
|
||||
|
||||
@Test
|
||||
fun `does not load any default rule set provider when opt out`() {
|
||||
val providers = createNullLoggingSpec { extensions { disableDefaultRuleSets = true } }
|
||||
.withSettings { RuleSetLocator(this).load() }
|
||||
val defaultProviders = getProviderClasses().toSet()
|
||||
|
||||
val defaultProviders = getProviderClasses().toSet()
|
||||
|
||||
assertThat(providers).noneMatch { it.javaClass in defaultProviders }
|
||||
}
|
||||
assertThat(providers).noneMatch { it.javaClass in defaultProviders }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,49 +13,45 @@ import org.junit.jupiter.api.Test
|
||||
class RuleSetSpec {
|
||||
|
||||
@Nested
|
||||
inner class `rule sets` {
|
||||
inner class `should rule set be used` {
|
||||
|
||||
@Nested
|
||||
inner class `should rule set be used` {
|
||||
|
||||
@Test
|
||||
fun `is explicitly deactivated`() {
|
||||
val config = yamlConfig("configs/deactivated_ruleset.yml")
|
||||
assertThat(config.subConfig("comments").isActive()).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `is active with an empty config`() {
|
||||
assertThat(Config.empty.isActive()).isTrue()
|
||||
}
|
||||
@Test
|
||||
fun `is explicitly deactivated`() {
|
||||
val config = yamlConfig("configs/deactivated_ruleset.yml")
|
||||
assertThat(config.subConfig("comments").isActive()).isFalse()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `should rule analyze a file` {
|
||||
@Test
|
||||
fun `is active with an empty config`() {
|
||||
assertThat(Config.empty.isActive()).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
private val file = compileForTest(resourceAsPath("/cases/Default.kt"))
|
||||
@Nested
|
||||
inner class `should rule analyze a file` {
|
||||
|
||||
@Test
|
||||
fun `analyzes file with an empty config`() {
|
||||
val config = Config.empty
|
||||
assertThat(config.subConfig("comments").shouldAnalyzeFile(file)).isTrue()
|
||||
}
|
||||
private val file = compileForTest(resourceAsPath("/cases/Default.kt"))
|
||||
|
||||
@Test
|
||||
@DisplayName("should not analyze file with **/*.kt excludes")
|
||||
fun ignoreExcludedKt() {
|
||||
val config = TestConfig(Config.EXCLUDES_KEY to "**/*.kt")
|
||||
assertThat(config.subConfig("comments").shouldAnalyzeFile(file)).isFalse()
|
||||
}
|
||||
@Test
|
||||
fun `analyzes file with an empty config`() {
|
||||
val config = Config.empty
|
||||
assertThat(config.subConfig("comments").shouldAnalyzeFile(file)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should analyze file as it's path is first excluded but then included`() {
|
||||
val config = TestConfig(
|
||||
Config.EXCLUDES_KEY to "**/*.kt",
|
||||
Config.INCLUDES_KEY to "**/*.kt"
|
||||
)
|
||||
assertThat(config.subConfig("comments").shouldAnalyzeFile(file)).isTrue()
|
||||
}
|
||||
@Test
|
||||
@DisplayName("should not analyze file with **/*.kt excludes")
|
||||
fun ignoreExcludedKt() {
|
||||
val config = TestConfig(Config.EXCLUDES_KEY to "**/*.kt")
|
||||
assertThat(config.subConfig("comments").shouldAnalyzeFile(file)).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should analyze file as it's path is first excluded but then included`() {
|
||||
val config = TestConfig(
|
||||
Config.EXCLUDES_KEY to "**/*.kt",
|
||||
Config.INCLUDES_KEY to "**/*.kt"
|
||||
)
|
||||
assertThat(config.subConfig("comments").shouldAnalyzeFile(file)).isTrue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,50 +9,41 @@ import io.gitlab.arturbosch.detekt.api.RuleSetProvider
|
||||
import io.gitlab.arturbosch.detekt.api.Severity
|
||||
import io.gitlab.arturbosch.detekt.test.yamlConfigFromContent
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import org.junit.jupiter.params.provider.ValueSource
|
||||
|
||||
class SingleRuleProviderSpec {
|
||||
|
||||
@Nested
|
||||
inner class `SingleRuleProvider` {
|
||||
|
||||
private val provider = SingleRuleProvider(
|
||||
"MagicNumber",
|
||||
object : RuleSetProvider {
|
||||
override val ruleSetId: String = "style"
|
||||
override fun instance(config: Config): RuleSet {
|
||||
val rule = object : Rule(config) {
|
||||
override val issue = Issue(
|
||||
"MagicNumber",
|
||||
Severity.CodeSmell,
|
||||
"",
|
||||
Debt.FIVE_MINS
|
||||
)
|
||||
}
|
||||
return RuleSet(ruleSetId, listOf(rule))
|
||||
private val provider = SingleRuleProvider(
|
||||
"MagicNumber",
|
||||
object : RuleSetProvider {
|
||||
override val ruleSetId: String = "style"
|
||||
override fun instance(config: Config): RuleSet {
|
||||
val rule = object : Rule(config) {
|
||||
override val issue = Issue(
|
||||
"MagicNumber",
|
||||
Severity.CodeSmell,
|
||||
"",
|
||||
Debt.FIVE_MINS
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@Nested
|
||||
inner class `the right sub config is passed to the rule` {
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = [true, false])
|
||||
fun `configures rule with active=$value`(value: Boolean) {
|
||||
val config = yamlConfigFromContent(
|
||||
"""
|
||||
style:
|
||||
MagicNumber:
|
||||
active: $value
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
assertThat(produceRule(provider, config).active).isEqualTo(value)
|
||||
return RuleSet(ruleSetId, listOf(rule))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = [true, false])
|
||||
fun `the right sub config is passed to the rule configures rule with active=$value`(value: Boolean) {
|
||||
val config = yamlConfigFromContent(
|
||||
"""
|
||||
style:
|
||||
MagicNumber:
|
||||
active: $value
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
assertThat(produceRule(provider, config).active).isEqualTo(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,25 +4,20 @@ import io.gitlab.arturbosch.detekt.core.createNullLoggingSpec
|
||||
import io.gitlab.arturbosch.detekt.core.createProcessingSettings
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class EnvironmentFacadeSpec {
|
||||
|
||||
@Nested
|
||||
inner class `classpath entries should be separated by platform-specific separator` {
|
||||
private val classpath = when (File.pathSeparator) {
|
||||
":" -> "/path/to/file1:/path/to/file2:/path/to/file3"
|
||||
";" -> """C:\path\to\file1;C:\path\to\file2;C:\path\to\file3"""
|
||||
else -> ""
|
||||
}
|
||||
|
||||
val classpath = when (File.pathSeparator) {
|
||||
":" -> "/path/to/file1:/path/to/file2:/path/to/file3"
|
||||
";" -> """C:\path\to\file1;C:\path\to\file2;C:\path\to\file3"""
|
||||
else -> ""
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `supports ${File_pathSeparator}`() {
|
||||
testSettings(classpath).use {
|
||||
assertThat(it.classpath).hasSize(3)
|
||||
}
|
||||
@Test
|
||||
fun `classpath entries should be separated by platform-specific separator supports ${File_pathSeparator}`() {
|
||||
testSettings(classpath).use {
|
||||
assertThat(it.classpath).hasSize(3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,57 +4,52 @@ import io.gitlab.arturbosch.detekt.api.Config
|
||||
import io.gitlab.arturbosch.detekt.formatting.wrappers.ArgumentListWrapping
|
||||
import io.gitlab.arturbosch.detekt.test.TestConfig
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class ArgumentListWrappingSpec {
|
||||
|
||||
@Nested
|
||||
inner class `ArgumentListWrapping rule` {
|
||||
@Test
|
||||
fun `reports wrong argument wrapping`() {
|
||||
val code = """
|
||||
val x = f(
|
||||
1,
|
||||
2, 3
|
||||
)
|
||||
""".trimIndent()
|
||||
assertThat(ArgumentListWrapping(Config.empty).lint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports wrong argument wrapping`() {
|
||||
val code = """
|
||||
val x = f(
|
||||
1,
|
||||
2, 3
|
||||
)
|
||||
""".trimIndent()
|
||||
assertThat(ArgumentListWrapping(Config.empty).lint(code)).hasSize(1)
|
||||
}
|
||||
@Test
|
||||
fun `does not report correct argument list wrapping`() {
|
||||
val code = """
|
||||
val x = f(
|
||||
1,
|
||||
2,
|
||||
3
|
||||
)
|
||||
""".trimIndent()
|
||||
assertThat(ArgumentListWrapping(Config.empty).lint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report correct argument list wrapping`() {
|
||||
val code = """
|
||||
val x = f(
|
||||
1,
|
||||
2,
|
||||
3
|
||||
)
|
||||
""".trimIndent()
|
||||
assertThat(ArgumentListWrapping(Config.empty).lint(code)).isEmpty()
|
||||
}
|
||||
@Test
|
||||
fun `does not report when overriding an indentation level config of 1`() {
|
||||
val code = """
|
||||
val x = f(
|
||||
1,
|
||||
2,
|
||||
3
|
||||
)
|
||||
""".trimIndent()
|
||||
val config = TestConfig("indentSize" to "1")
|
||||
assertThat(ArgumentListWrapping(config).lint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report when overriding an indentation level config of 1`() {
|
||||
val code = """
|
||||
val x = f(
|
||||
1,
|
||||
2,
|
||||
3
|
||||
)
|
||||
""".trimIndent()
|
||||
val config = TestConfig("indentSize" to "1")
|
||||
assertThat(ArgumentListWrapping(config).lint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports when max line length is exceeded`() {
|
||||
val code = """
|
||||
val x = f(1111, 2222, 3333)
|
||||
""".trimIndent()
|
||||
val config = TestConfig("maxLineLength" to "10")
|
||||
assertThat(ArgumentListWrapping(config).lint(code)).hasSize(4)
|
||||
}
|
||||
@Test
|
||||
fun `reports when max line length is exceeded`() {
|
||||
val code = """
|
||||
val x = f(1111, 2222, 3333)
|
||||
""".trimIndent()
|
||||
val config = TestConfig("maxLineLength" to "10")
|
||||
assertThat(ArgumentListWrapping(config).lint(code)).hasSize(4)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,69 +6,48 @@ import io.gitlab.arturbosch.detekt.test.loadRuleSet
|
||||
import io.gitlab.arturbosch.detekt.test.yamlConfig
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class AutoCorrectLevelSpec {
|
||||
|
||||
@Nested
|
||||
inner class `test different autoCorrect levels in configuration` {
|
||||
@Test
|
||||
fun `autoCorrect_ true on all levels should reformat the test file`() {
|
||||
val config = yamlConfig("/autocorrect/autocorrect-all-true.yml")
|
||||
|
||||
@Nested
|
||||
inner class `autoCorrect_ true on all levels` {
|
||||
val (file, findings) = runRule(config)
|
||||
|
||||
@Test
|
||||
fun `should reformat the test file`() {
|
||||
val config = yamlConfig("/autocorrect/autocorrect-all-true.yml")
|
||||
assertThat(wasLinted(findings)).isTrue()
|
||||
assertThat(wasFormatted(file)).isTrue()
|
||||
}
|
||||
|
||||
val (file, findings) = runRule(config)
|
||||
@Test
|
||||
fun `autoCorrect_ false on ruleSet level should not reformat the test file`() {
|
||||
val config = yamlConfig("/autocorrect/autocorrect-ruleset-false.yml")
|
||||
|
||||
assertThat(wasLinted(findings)).isTrue()
|
||||
assertThat(wasFormatted(file)).isTrue()
|
||||
}
|
||||
}
|
||||
val (file, findings) = runRule(config)
|
||||
|
||||
@Nested
|
||||
inner class `autoCorrect_ false on ruleSet level` {
|
||||
assertThat(wasLinted(findings)).isTrue()
|
||||
assertThat(wasFormatted(file)).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not reformat the test file`() {
|
||||
val config = yamlConfig("/autocorrect/autocorrect-ruleset-false.yml")
|
||||
@Test
|
||||
fun `autoCorrect_ false on rule level should not reformat the test file`() {
|
||||
val config = yamlConfig("/autocorrect/autocorrect-rule-false.yml")
|
||||
|
||||
val (file, findings) = runRule(config)
|
||||
val (file, findings) = runRule(config)
|
||||
|
||||
assertThat(wasLinted(findings)).isTrue()
|
||||
assertThat(wasFormatted(file)).isFalse()
|
||||
}
|
||||
}
|
||||
assertThat(wasLinted(findings)).isTrue()
|
||||
assertThat(wasFormatted(file)).isFalse()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `autoCorrect_ false on rule level` {
|
||||
@Test
|
||||
fun `autoCorrect_ true but rule active false should not reformat the test file`() {
|
||||
val config = yamlConfig("/autocorrect/autocorrect-true-rule-active-false.yml")
|
||||
|
||||
@Test
|
||||
fun `should not reformat the test file`() {
|
||||
val config = yamlConfig("/autocorrect/autocorrect-rule-false.yml")
|
||||
val (file, findings) = runRule(config)
|
||||
|
||||
val (file, findings) = runRule(config)
|
||||
|
||||
assertThat(wasLinted(findings)).isTrue()
|
||||
assertThat(wasFormatted(file)).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `autoCorrect_ true but rule active false` {
|
||||
|
||||
@Test
|
||||
fun `should not reformat the test file`() {
|
||||
val config = yamlConfig("/autocorrect/autocorrect-true-rule-active-false.yml")
|
||||
|
||||
val (file, findings) = runRule(config)
|
||||
|
||||
assertThat(wasLinted(findings)).isFalse()
|
||||
assertThat(wasFormatted(file)).isFalse()
|
||||
}
|
||||
}
|
||||
assertThat(wasLinted(findings)).isFalse()
|
||||
assertThat(wasFormatted(file)).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,23 +3,18 @@ package io.gitlab.arturbosch.detekt.formatting
|
||||
import io.gitlab.arturbosch.detekt.api.Config
|
||||
import io.gitlab.arturbosch.detekt.formatting.wrappers.ChainWrapping
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class ChainWrappingSpec {
|
||||
|
||||
@Nested
|
||||
inner class `tests integration of formatting` {
|
||||
@Test
|
||||
fun `should work like KtLint`() {
|
||||
val subject = loadFile("configTests/chain-wrapping-before.kt")
|
||||
val expected = loadFileContent("configTests/chain-wrapping-after.kt")
|
||||
|
||||
@Test
|
||||
fun `should work like KtLint`() {
|
||||
val subject = loadFile("configTests/chain-wrapping-before.kt")
|
||||
val expected = loadFileContent("configTests/chain-wrapping-after.kt")
|
||||
val findings = ChainWrapping(Config.empty).lint(subject.text)
|
||||
|
||||
val findings = ChainWrapping(Config.empty).lint(subject.text)
|
||||
|
||||
assertThat(findings).isNotEmpty
|
||||
assertThat(subject.text).isEqualTo(expected)
|
||||
}
|
||||
assertThat(findings).isNotEmpty
|
||||
assertThat(subject.text).isEqualTo(expected)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,54 +4,49 @@ import io.gitlab.arturbosch.detekt.api.Config
|
||||
import io.gitlab.arturbosch.detekt.formatting.wrappers.FinalNewline
|
||||
import io.gitlab.arturbosch.detekt.test.TestConfig
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class FinalNewlineSpec {
|
||||
|
||||
@Nested
|
||||
inner class `FinalNewline rule` {
|
||||
@Test
|
||||
fun `should report missing new line by default`() {
|
||||
val findings = FinalNewline(Config.empty)
|
||||
.lint("fun main() = Unit")
|
||||
|
||||
@Test
|
||||
fun `should report missing new line by default`() {
|
||||
val findings = FinalNewline(Config.empty)
|
||||
.lint("fun main() = Unit")
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
@Test
|
||||
fun `should not report as new line is present`() {
|
||||
val findings = FinalNewline(Config.empty).lint(
|
||||
"""
|
||||
fun main() = Unit
|
||||
|
||||
@Test
|
||||
fun `should not report as new line is present`() {
|
||||
val findings = FinalNewline(Config.empty).lint(
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should report new line when configured`() {
|
||||
val findings = FinalNewline(TestConfig(INSERT_FINAL_NEWLINE_KEY to "false"))
|
||||
.lint(
|
||||
"""
|
||||
fun main() = Unit
|
||||
fun main() = Unit
|
||||
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should report new line when configured`() {
|
||||
val findings = FinalNewline(TestConfig(INSERT_FINAL_NEWLINE_KEY to "false"))
|
||||
.lint(
|
||||
"""
|
||||
fun main() = Unit
|
||||
@Test
|
||||
fun `should not report when no new line is configured and not present`() {
|
||||
val findings = FinalNewline(TestConfig(INSERT_FINAL_NEWLINE_KEY to "false"))
|
||||
.lint("fun main() = Unit")
|
||||
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not report when no new line is configured and not present`() {
|
||||
val findings = FinalNewline(TestConfig(INSERT_FINAL_NEWLINE_KEY to "false"))
|
||||
.lint("fun main() = Unit")
|
||||
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,22 +16,103 @@ import org.junit.jupiter.api.Test
|
||||
*/
|
||||
class ImportOrderingSpec {
|
||||
|
||||
@Test
|
||||
fun `defaults to the idea layout`() {
|
||||
val findings = ImportOrdering(Config.empty).lint(
|
||||
"""
|
||||
import android.app.Activity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import ru.example.a
|
||||
import java.util.List
|
||||
import javax.net.ssl.SSLHandshakeException
|
||||
import kotlin.concurrent.Thread
|
||||
import kotlin.io.Closeable
|
||||
import android.content.Context as Ctx
|
||||
import androidx.fragment.app.Fragment as F
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `different import ordering layouts` {
|
||||
inner class `can be configured to use the ascii one` {
|
||||
|
||||
val negativeCase = """
|
||||
import a.A
|
||||
import a.AB
|
||||
import b.C
|
||||
""".trimIndent()
|
||||
|
||||
val positiveCase = """
|
||||
import a.A
|
||||
import java.util.ArrayList
|
||||
import a.AB
|
||||
""".trimIndent()
|
||||
|
||||
@Test
|
||||
fun `defaults to the idea layout`() {
|
||||
val findings = ImportOrdering(Config.empty).lint(
|
||||
fun `passes for alphabetical order`() {
|
||||
val findings = ImportOrdering(TestConfig("layout" to ImportOrdering.ASCII_PATTERN))
|
||||
.lint(negativeCase)
|
||||
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fails for non alphabetical order`() {
|
||||
val findings = ImportOrdering(TestConfig("layout" to ImportOrdering.ASCII_PATTERN))
|
||||
.lint(positiveCase)
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `defaults to ascii if 'android'' property is set to true` {
|
||||
|
||||
@Test
|
||||
fun `passes for alphabetical order`() {
|
||||
assertThat(ImportOrdering(TestConfig("android" to "true")).lint(negativeCase)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fails for non alphabetical order`() {
|
||||
assertThat(ImportOrdering(TestConfig("android" to "true")).lint(positiveCase)).hasSize(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `supports custom patterns` {
|
||||
|
||||
@Test
|
||||
fun `misses a empty line between aliases and other imports`() {
|
||||
val findings = ImportOrdering(TestConfig("layout" to "*,|,^*")).lint(
|
||||
"""
|
||||
import android.app.Activity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import ru.example.a
|
||||
import java.util.List
|
||||
import javax.net.ssl.SSLHandshakeException
|
||||
import kotlin.concurrent.Thread
|
||||
import kotlin.io.Closeable
|
||||
import android.content.Context as Ctx
|
||||
import androidx.fragment.app.Fragment as F
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `passes for empty line between aliases and other imports`() {
|
||||
val findings = ImportOrdering(TestConfig("layout" to "*,|,^*")).lint(
|
||||
"""
|
||||
import android.app.Activity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import java.util.List
|
||||
import kotlin.concurrent.Thread
|
||||
|
||||
import android.content.Context as Ctx
|
||||
import androidx.fragment.app.Fragment as F
|
||||
""".trimIndent()
|
||||
@@ -39,90 +120,5 @@ class ImportOrderingSpec {
|
||||
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `can be configured to use the ascii one` {
|
||||
|
||||
val negativeCase = """
|
||||
import a.A
|
||||
import a.AB
|
||||
import b.C
|
||||
""".trimIndent()
|
||||
|
||||
val positiveCase = """
|
||||
import a.A
|
||||
import java.util.ArrayList
|
||||
import a.AB
|
||||
""".trimIndent()
|
||||
|
||||
@Test
|
||||
fun `passes for alphabetical order`() {
|
||||
val findings = ImportOrdering(TestConfig("layout" to ImportOrdering.ASCII_PATTERN))
|
||||
.lint(negativeCase)
|
||||
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fails for non alphabetical order`() {
|
||||
val findings = ImportOrdering(TestConfig("layout" to ImportOrdering.ASCII_PATTERN))
|
||||
.lint(positiveCase)
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `defaults to ascii if 'android'' property is set to true` {
|
||||
|
||||
@Test
|
||||
fun `passes for alphabetical order`() {
|
||||
assertThat(ImportOrdering(TestConfig("android" to "true")).lint(negativeCase)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fails for non alphabetical order`() {
|
||||
assertThat(ImportOrdering(TestConfig("android" to "true")).lint(positiveCase)).hasSize(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `supports custom patterns` {
|
||||
|
||||
@Test
|
||||
fun `misses a empty line between aliases and other imports`() {
|
||||
val findings = ImportOrdering(TestConfig("layout" to "*,|,^*")).lint(
|
||||
"""
|
||||
import android.app.Activity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import java.util.List
|
||||
import kotlin.concurrent.Thread
|
||||
import android.content.Context as Ctx
|
||||
import androidx.fragment.app.Fragment as F
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `passes for empty line between aliases and other imports`() {
|
||||
val findings = ImportOrdering(TestConfig("layout" to "*,|,^*")).lint(
|
||||
"""
|
||||
import android.app.Activity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import java.util.List
|
||||
import kotlin.concurrent.Thread
|
||||
|
||||
import android.content.Context as Ctx
|
||||
import androidx.fragment.app.Fragment as F
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,61 +19,57 @@ class IndentationSpec {
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `Indentation rule` {
|
||||
inner class `indentation level equals 1` {
|
||||
|
||||
val code = "fun main() {\n println()\n}"
|
||||
|
||||
@Nested
|
||||
inner class `indentation level equals 1` {
|
||||
|
||||
val code = "fun main() {\n println()\n}"
|
||||
|
||||
@Nested
|
||||
inner class `indentation level config of default` {
|
||||
|
||||
@Test
|
||||
fun `reports wrong indentation level`() {
|
||||
assertThat(subject.lint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `places finding location to the indentation`() {
|
||||
subject.lint(code).assert()
|
||||
.hasSourceLocation(2, 1)
|
||||
.hasTextLocations(13 to 14)
|
||||
}
|
||||
}
|
||||
inner class `indentation level config of default` {
|
||||
|
||||
@Test
|
||||
fun `does not report when using an indentation level config of 1`() {
|
||||
val config = TestConfig("indentSize" to "1")
|
||||
assertThat(Indentation(config).lint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report correct indentation level`() {
|
||||
val code = "fun main() {\n println()\n}"
|
||||
assertThat(subject.lint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `parameter list indent size equals 1` {
|
||||
|
||||
val code = """
|
||||
fun f(
|
||||
a: Int
|
||||
) {}
|
||||
""".trimIndent()
|
||||
|
||||
@Test
|
||||
fun `reports wrong indent size`() {
|
||||
fun `reports wrong indentation level`() {
|
||||
assertThat(subject.lint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report when using an indentation level config of 1`() {
|
||||
val config = TestConfig("indentSize" to "1")
|
||||
assertThat(Indentation(config).lint(code)).isEmpty()
|
||||
fun `places finding location to the indentation`() {
|
||||
subject.lint(code).assert()
|
||||
.hasSourceLocation(2, 1)
|
||||
.hasTextLocations(13 to 14)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report when using an indentation level config of 1`() {
|
||||
val config = TestConfig("indentSize" to "1")
|
||||
assertThat(Indentation(config).lint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report correct indentation level`() {
|
||||
val code = "fun main() {\n println()\n}"
|
||||
assertThat(subject.lint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `parameter list indent size equals 1` {
|
||||
|
||||
val code = """
|
||||
fun f(
|
||||
a: Int
|
||||
) {}
|
||||
""".trimIndent()
|
||||
|
||||
@Test
|
||||
fun `reports wrong indent size`() {
|
||||
assertThat(subject.lint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report when using an indentation level config of 1`() {
|
||||
val config = TestConfig("indentSize" to "1")
|
||||
assertThat(Indentation(config).lint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,29 +3,24 @@ package io.gitlab.arturbosch.detekt.formatting
|
||||
import io.github.detekt.test.utils.compileContentForTest
|
||||
import io.gitlab.arturbosch.detekt.api.Config
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class KtLintMultiRuleSpec {
|
||||
|
||||
@Nested
|
||||
inner class `KtLintMultiRule rule` {
|
||||
|
||||
@Test
|
||||
fun `sorts rules correctly`() {
|
||||
val ktlintRule = KtLintMultiRule(Config.empty)
|
||||
ktlintRule.visitFile(compileContentForTest(""))
|
||||
val sortedRules = ktlintRule.getSortedRules()
|
||||
assertThat(sortedRules).isNotEmpty
|
||||
assertThat(sortedRules.indexOfFirst { it.runOnRootNodeOnly })
|
||||
.isGreaterThan(-1)
|
||||
.isLessThan(sortedRules.indexOfFirst { !it.runOnRootNodeOnly })
|
||||
assertThat(sortedRules.indexOfFirst { !it.runOnRootNodeOnly })
|
||||
.isGreaterThan(-1)
|
||||
.isLessThan(sortedRules.indexOfFirst { it.runOnRootNodeOnly && it.runAsLateAsPossible })
|
||||
assertThat(sortedRules.indexOfFirst { it.runOnRootNodeOnly && it.runAsLateAsPossible })
|
||||
.isGreaterThan(-1)
|
||||
.isLessThan(sortedRules.indexOfFirst { it.runAsLateAsPossible && !it.runOnRootNodeOnly })
|
||||
}
|
||||
@Test
|
||||
fun `sorts rules correctly`() {
|
||||
val ktlintRule = KtLintMultiRule(Config.empty)
|
||||
ktlintRule.visitFile(compileContentForTest(""))
|
||||
val sortedRules = ktlintRule.getSortedRules()
|
||||
assertThat(sortedRules).isNotEmpty
|
||||
assertThat(sortedRules.indexOfFirst { it.runOnRootNodeOnly })
|
||||
.isGreaterThan(-1)
|
||||
.isLessThan(sortedRules.indexOfFirst { !it.runOnRootNodeOnly })
|
||||
assertThat(sortedRules.indexOfFirst { !it.runOnRootNodeOnly })
|
||||
.isGreaterThan(-1)
|
||||
.isLessThan(sortedRules.indexOfFirst { it.runOnRootNodeOnly && it.runAsLateAsPossible })
|
||||
assertThat(sortedRules.indexOfFirst { it.runOnRootNodeOnly && it.runAsLateAsPossible })
|
||||
.isGreaterThan(-1)
|
||||
.isLessThan(sortedRules.indexOfFirst { it.runAsLateAsPossible && !it.runOnRootNodeOnly })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,71 +18,67 @@ class MaximumLineLengthSpec {
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `MaximumLineLength rule` {
|
||||
inner class `a single function` {
|
||||
|
||||
@Nested
|
||||
inner class `a single function` {
|
||||
val code = """
|
||||
package home.test
|
||||
fun f() { /* 123456789012345678901234567890 */ }
|
||||
""".trimIndent()
|
||||
|
||||
val code = """
|
||||
package home.test
|
||||
fun f() { /* 123456789012345678901234567890 */ }
|
||||
""".trimIndent()
|
||||
@Test
|
||||
fun `reports line which exceeds the threshold`() {
|
||||
assertThat(subject.lint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports line which exceeds the threshold`() {
|
||||
assertThat(subject.lint(code)).hasSize(1)
|
||||
}
|
||||
@Test
|
||||
fun `reports issues with the filename and package as signature`() {
|
||||
val finding = subject.lint(
|
||||
code,
|
||||
Paths.get("home", "test", "Test.kt").toString()
|
||||
).first()
|
||||
|
||||
@Test
|
||||
fun `reports issues with the filename and package as signature`() {
|
||||
val finding = subject.lint(
|
||||
code,
|
||||
Paths.get("home", "test", "Test.kt").toString()
|
||||
).first()
|
||||
|
||||
assertThat(finding.entity.signature).isEqualTo("home.test.Test.kt:2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report line which does not exceed the threshold`() {
|
||||
val config = TestConfig(MAX_LINE_LENGTH to code.length)
|
||||
assertThat(MaximumLineLength(config).lint(code)).isEmpty()
|
||||
}
|
||||
assertThat(finding.entity.signature).isEqualTo("home.test.Test.kt:2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report line which does not exceed the threshold`() {
|
||||
val code = "val a = 1"
|
||||
assertThat(subject.lint(code)).isEmpty()
|
||||
val config = TestConfig(MAX_LINE_LENGTH to code.length)
|
||||
assertThat(MaximumLineLength(config).lint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports correct line numbers`() {
|
||||
val findings = subject.lint(longLines)
|
||||
@Test
|
||||
fun `does not report line which does not exceed the threshold`() {
|
||||
val code = "val a = 1"
|
||||
assertThat(subject.lint(code)).isEmpty()
|
||||
}
|
||||
|
||||
// Note that KtLint's MaximumLineLength rule, in contrast to detekt's MaxLineLength rule, does not report
|
||||
// exceeded lines in block comments.
|
||||
assertThat(findings).hasSize(2)
|
||||
@Test
|
||||
fun `reports correct line numbers`() {
|
||||
val findings = subject.lint(longLines)
|
||||
|
||||
assertThat(findings[0].entity.location.source.line).isEqualTo(8)
|
||||
assertThat(findings[1].entity.location.source.line).isEqualTo(14)
|
||||
}
|
||||
// Note that KtLint's MaximumLineLength rule, in contrast to detekt's MaxLineLength rule, does not report
|
||||
// exceeded lines in block comments.
|
||||
assertThat(findings).hasSize(2)
|
||||
|
||||
@Test
|
||||
fun `does not report back ticked line which exceeds the threshold`() {
|
||||
val code = """
|
||||
package home.test
|
||||
fun `this is a test method that has more than 30 characters inside the back ticks`() {
|
||||
}
|
||||
""".trimIndent()
|
||||
val findings = MaximumLineLength(
|
||||
TestConfig(
|
||||
MAX_LINE_LENGTH to "30",
|
||||
"ignoreBackTickedIdentifier" to "true"
|
||||
)
|
||||
).lint(code)
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
assertThat(findings[0].entity.location.source.line).isEqualTo(8)
|
||||
assertThat(findings[1].entity.location.source.line).isEqualTo(14)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report back ticked line which exceeds the threshold`() {
|
||||
val code = """
|
||||
package home.test
|
||||
fun `this is a test method that has more than 30 characters inside the back ticks`() {
|
||||
}
|
||||
""".trimIndent()
|
||||
val findings = MaximumLineLength(
|
||||
TestConfig(
|
||||
MAX_LINE_LENGTH to "30",
|
||||
"ignoreBackTickedIdentifier" to "true"
|
||||
)
|
||||
).lint(code)
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import io.gitlab.arturbosch.detekt.formatting.wrappers.ParameterListWrapping
|
||||
import io.gitlab.arturbosch.detekt.test.TestConfig
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class ParameterListWrappingSpec {
|
||||
@@ -17,26 +16,22 @@ class ParameterListWrappingSpec {
|
||||
subject = ParameterListWrapping(Config.empty)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `ParameterListWrapping rule` {
|
||||
@Test
|
||||
fun `does not report correct ParameterListWrapping level`() {
|
||||
val code = """
|
||||
fun f(
|
||||
a: Int
|
||||
) {}
|
||||
""".trimIndent()
|
||||
assertThat(subject.lint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report correct ParameterListWrapping level`() {
|
||||
val code = """
|
||||
fun f(
|
||||
a: Int
|
||||
) {}
|
||||
""".trimIndent()
|
||||
assertThat(subject.lint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports when max line length is exceeded`() {
|
||||
val code = """
|
||||
fun f(a: Int, b: Int, c: Int) {}
|
||||
""".trimIndent()
|
||||
val config = TestConfig("maxLineLength" to "10")
|
||||
assertThat(ParameterListWrapping(config).lint(code)).hasSize(4)
|
||||
}
|
||||
@Test
|
||||
fun `reports when max line length is exceeded`() {
|
||||
val code = """
|
||||
fun f(a: Int, b: Int, c: Int) {}
|
||||
""".trimIndent()
|
||||
val config = TestConfig("maxLineLength" to "10")
|
||||
assertThat(ParameterListWrapping(config).lint(code)).hasSize(4)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,65 +17,62 @@ private val defaultConfiguration = Configuration(
|
||||
class ConfigurationSpec {
|
||||
|
||||
@Nested
|
||||
inner class `default value to list conversion` {
|
||||
@Nested
|
||||
inner class `empty default value` {
|
||||
private val subject = defaultConfiguration.copy(defaultValue = of(""))
|
||||
inner class `empty default value` {
|
||||
private val subject = defaultConfiguration.copy(defaultValue = of(""))
|
||||
|
||||
@Test
|
||||
fun `identifies default as not a list`() {
|
||||
assertThat(subject.isDefaultValueNonEmptyList()).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fails when attempting conversion`() {
|
||||
assertThatIllegalStateException().isThrownBy { subject.getDefaultValueAsList() }
|
||||
}
|
||||
@Test
|
||||
fun `identifies default as not a list`() {
|
||||
assertThat(subject.isDefaultValueNonEmptyList()).isFalse()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `non list default value` {
|
||||
private val subject = defaultConfiguration.copy(defaultValue = of("abc"))
|
||||
@Test
|
||||
fun `fails when attempting conversion`() {
|
||||
assertThatIllegalStateException().isThrownBy { subject.getDefaultValueAsList() }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `identifies default as not a list`() {
|
||||
assertThat(subject.isDefaultValueNonEmptyList()).isFalse()
|
||||
}
|
||||
@Nested
|
||||
inner class `non list default value` {
|
||||
private val subject = defaultConfiguration.copy(defaultValue = of("abc"))
|
||||
|
||||
@Test
|
||||
fun `fails when attempting conversion`() {
|
||||
assertThatIllegalStateException().isThrownBy { subject.getDefaultValueAsList() }
|
||||
}
|
||||
@Test
|
||||
fun `identifies default as not a list`() {
|
||||
assertThat(subject.isDefaultValueNonEmptyList()).isFalse()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `empty list default value` {
|
||||
private val subject = defaultConfiguration.copy(defaultValue = of(emptyList()))
|
||||
@Test
|
||||
fun `fails when attempting conversion`() {
|
||||
assertThatIllegalStateException().isThrownBy { subject.getDefaultValueAsList() }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `identifies default as not a non empty list`() {
|
||||
assertThat(subject.isDefaultValueNonEmptyList()).isFalse()
|
||||
}
|
||||
@Nested
|
||||
inner class `empty list default value` {
|
||||
private val subject = defaultConfiguration.copy(defaultValue = of(emptyList()))
|
||||
|
||||
@Test
|
||||
fun `fails when attempting conversion`() {
|
||||
assertThatIllegalStateException().isThrownBy { subject.getDefaultValueAsList() }
|
||||
}
|
||||
@Test
|
||||
fun `identifies default as not a non empty list`() {
|
||||
assertThat(subject.isDefaultValueNonEmptyList()).isFalse()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `bracket list default value` {
|
||||
private val subject = defaultConfiguration.copy(defaultValue = of(listOf("a", "b")))
|
||||
@Test
|
||||
fun `fails when attempting conversion`() {
|
||||
assertThatIllegalStateException().isThrownBy { subject.getDefaultValueAsList() }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `identifies default as a non empty list`() {
|
||||
assertThat(subject.isDefaultValueNonEmptyList()).isTrue()
|
||||
}
|
||||
@Nested
|
||||
inner class `bracket list default value` {
|
||||
private val subject = defaultConfiguration.copy(defaultValue = of(listOf("a", "b")))
|
||||
|
||||
@Test
|
||||
fun `converts to a list`() {
|
||||
assertThat(subject.getDefaultValueAsList()).isEqualTo(listOf("a", "b"))
|
||||
}
|
||||
@Test
|
||||
fun `identifies default as a non empty list`() {
|
||||
assertThat(subject.isDefaultValueNonEmptyList()).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `converts to a list`() {
|
||||
assertThat(subject.getDefaultValueAsList()).isEqualTo(listOf("a", "b"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import io.gitlab.arturbosch.detekt.generator.util.run
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class MultiRuleCollectorSpec {
|
||||
@@ -17,47 +16,43 @@ class MultiRuleCollectorSpec {
|
||||
subject = MultiRuleCollector()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `a MultiRuleCollector` {
|
||||
@Test
|
||||
fun `collects no MultiRule when no class is extended`() {
|
||||
val code = "class MyRule"
|
||||
assertThat(subject.run(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `collects no MultiRule when no class is extended`() {
|
||||
val code = "class MyRule"
|
||||
assertThat(subject.run(code)).isEmpty()
|
||||
}
|
||||
@Test
|
||||
fun `collects no rules when no MultiRule class is extended`() {
|
||||
val code = "class MyRule : Other"
|
||||
assertThat(subject.run(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `collects no rules when no MultiRule class is extended`() {
|
||||
val code = "class MyRule : Other"
|
||||
assertThat(subject.run(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `throws when no rules are added`() {
|
||||
val code = "class MyRule : MultiRule"
|
||||
assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy {
|
||||
subject.run(code)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `collects all rules in fields and in the rule property`() {
|
||||
val code = """
|
||||
class MyRule : MultiRule {
|
||||
val p1 = Rule3()
|
||||
val p2 = Rule4()
|
||||
|
||||
override val rules: List<Rule> = listOf(
|
||||
Rule1(),
|
||||
Rule2(),
|
||||
p1,
|
||||
p2
|
||||
)
|
||||
}
|
||||
"""
|
||||
val items = subject.run(code)
|
||||
assertThat(items[0].rules).hasSize(4)
|
||||
assertThat(items[0].rules).contains("Rule1", "Rule2", "Rule3", "Rule4")
|
||||
@Test
|
||||
fun `throws when no rules are added`() {
|
||||
val code = "class MyRule : MultiRule"
|
||||
assertThatExceptionOfType(InvalidDocumentationException::class.java).isThrownBy {
|
||||
subject.run(code)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `collects all rules in fields and in the rule property`() {
|
||||
val code = """
|
||||
class MyRule : MultiRule {
|
||||
val p1 = Rule3()
|
||||
val p2 = Rule4()
|
||||
|
||||
override val rules: List<Rule> = listOf(
|
||||
Rule1(),
|
||||
Rule2(),
|
||||
p1,
|
||||
p2
|
||||
)
|
||||
}
|
||||
"""
|
||||
val items = subject.run(code)
|
||||
assertThat(items[0].rules).hasSize(4)
|
||||
assertThat(items[0].rules).contains("Rule1", "Rule2", "Rule3", "Rule4")
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,410 +20,407 @@ class RuleSetProviderCollectorSpec {
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `RuleSetProviderCollector rule` {
|
||||
@Nested
|
||||
inner class `a non-RuleSetProvider class extending nothing` {
|
||||
val code = """
|
||||
package foo
|
||||
inner class `a non-RuleSetProvider class extending nothing` {
|
||||
val code = """
|
||||
package foo
|
||||
|
||||
class SomeRandomClass {
|
||||
fun logSomething(message: String) {
|
||||
println(message)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `collects no rulesets`() {
|
||||
val items = subject.run(code)
|
||||
assertThat(items).isEmpty()
|
||||
class SomeRandomClass {
|
||||
fun logSomething(message: String) {
|
||||
println(message)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Nested
|
||||
inner class `a non-RuleSetProvider class extending a class that is not related to rules` {
|
||||
val code = """
|
||||
package foo
|
||||
@Test
|
||||
fun `collects no rulesets`() {
|
||||
val items = subject.run(code)
|
||||
assertThat(items).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
class SomeRandomClass: SomeOtherClass {
|
||||
fun logSomething(message: String) {
|
||||
println(message)
|
||||
}
|
||||
}
|
||||
"""
|
||||
@Nested
|
||||
inner class `a non-RuleSetProvider class extending a class that is not related to rules` {
|
||||
val code = """
|
||||
package foo
|
||||
|
||||
@Test
|
||||
fun `collects no rulesets`() {
|
||||
val items = subject.run(code)
|
||||
assertThat(items).isEmpty()
|
||||
class SomeRandomClass: SomeOtherClass {
|
||||
fun logSomething(message: String) {
|
||||
println(message)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Nested
|
||||
inner class `a RuleSetProvider without documentation` {
|
||||
val code = """
|
||||
package foo
|
||||
@Test
|
||||
fun `collects no rulesets`() {
|
||||
val items = subject.run(code)
|
||||
assertThat(items).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
class TestProvider: RuleSetProvider {
|
||||
fun logSomething(message: String) {
|
||||
println(message)
|
||||
}
|
||||
}
|
||||
"""
|
||||
@Nested
|
||||
inner class `a RuleSetProvider without documentation` {
|
||||
val code = """
|
||||
package foo
|
||||
|
||||
@Test
|
||||
fun `throws an exception`() {
|
||||
assertThatExceptionOfType(InvalidDocumentationException::class.java)
|
||||
.isThrownBy { subject.run(code) }
|
||||
class TestProvider: RuleSetProvider {
|
||||
fun logSomething(message: String) {
|
||||
println(message)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Nested
|
||||
inner class `a correct RuleSetProvider class extending RuleSetProvider but missing parameters` {
|
||||
val code = """
|
||||
package foo
|
||||
@Test
|
||||
fun `throws an exception`() {
|
||||
assertThatExceptionOfType(InvalidDocumentationException::class.java)
|
||||
.isThrownBy { subject.run(code) }
|
||||
}
|
||||
}
|
||||
|
||||
class TestProvider: RuleSetProvider {
|
||||
fun logSomething(message: String) {
|
||||
println(message)
|
||||
}
|
||||
}
|
||||
"""
|
||||
@Nested
|
||||
inner class `a correct RuleSetProvider class extending RuleSetProvider but missing parameters` {
|
||||
val code = """
|
||||
package foo
|
||||
|
||||
@Test
|
||||
fun `throws an exception`() {
|
||||
assertThatExceptionOfType(InvalidDocumentationException::class.java)
|
||||
.isThrownBy { subject.run(code) }
|
||||
class TestProvider: RuleSetProvider {
|
||||
fun logSomething(message: String) {
|
||||
println(message)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Nested
|
||||
inner class `a correct RuleSetProvider class with full parameters` {
|
||||
val description = "This is a description"
|
||||
val ruleSetId = "test"
|
||||
val ruleName = "TestRule"
|
||||
val code = """
|
||||
package foo
|
||||
@Test
|
||||
fun `throws an exception`() {
|
||||
assertThatExceptionOfType(InvalidDocumentationException::class.java)
|
||||
.isThrownBy { subject.run(code) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* $description
|
||||
*
|
||||
*
|
||||
*/
|
||||
@ActiveByDefault("1.0.0")
|
||||
class TestProvider: RuleSetProvider {
|
||||
override val ruleSetId: String = "$ruleSetId"
|
||||
@Nested
|
||||
inner class `a correct RuleSetProvider class with full parameters` {
|
||||
val description = "This is a description"
|
||||
val ruleSetId = "test"
|
||||
val ruleName = "TestRule"
|
||||
val code = """
|
||||
package foo
|
||||
|
||||
override fun instance(config: Config): RuleSet {
|
||||
return RuleSet(ruleSetId, listOf(
|
||||
$ruleName(config)
|
||||
))
|
||||
}
|
||||
}
|
||||
"""
|
||||
/**
|
||||
* $description
|
||||
*
|
||||
*
|
||||
*/
|
||||
@ActiveByDefault("1.0.0")
|
||||
class TestProvider: RuleSetProvider {
|
||||
override val ruleSetId: String = "$ruleSetId"
|
||||
|
||||
@Test
|
||||
fun `collects a RuleSetProvider`() {
|
||||
val items = subject.run(code)
|
||||
assertThat(items).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `has one rule`() {
|
||||
val items = subject.run(code)
|
||||
val provider = items[0]
|
||||
assertThat(provider.rules).hasSize(1)
|
||||
assertThat(provider.rules[0]).isEqualTo(ruleName)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `has correct name`() {
|
||||
val items = subject.run(code)
|
||||
val provider = items[0]
|
||||
assertThat(provider.name).isEqualTo(ruleSetId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `has correct description`() {
|
||||
val items = subject.run(code)
|
||||
val provider = items[0]
|
||||
assertThat(provider.description).isEqualTo(description)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `is active`() {
|
||||
val items = subject.run(code)
|
||||
val provider = items[0]
|
||||
assertThat(provider.defaultActivationStatus.active).isTrue()
|
||||
override fun instance(config: Config): RuleSet {
|
||||
return RuleSet(ruleSetId, listOf(
|
||||
$ruleName(config)
|
||||
))
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Nested
|
||||
inner class `an inactive RuleSetProvider` {
|
||||
val description = "This is a description"
|
||||
val ruleSetId = "test"
|
||||
val ruleName = "TestRule"
|
||||
val code = """
|
||||
package foo
|
||||
|
||||
/**
|
||||
* $description
|
||||
*/
|
||||
class TestProvider: RuleSetProvider {
|
||||
override val ruleSetId: String = "$ruleSetId"
|
||||
|
||||
override fun instance(config: Config): RuleSet {
|
||||
return RuleSet(ruleSetId, listOf(
|
||||
$ruleName(config)
|
||||
))
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `is not active`() {
|
||||
val items = subject.run(code)
|
||||
val provider = items[0]
|
||||
assertThat(provider.defaultActivationStatus.active).isFalse()
|
||||
}
|
||||
@Test
|
||||
fun `collects a RuleSetProvider`() {
|
||||
val items = subject.run(code)
|
||||
assertThat(items).hasSize(1)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `a RuleSetProvider with missing name` {
|
||||
val description = "This is a description"
|
||||
val ruleName = "TestRule"
|
||||
val code = """
|
||||
package foo
|
||||
|
||||
/**
|
||||
* $description
|
||||
*/
|
||||
class TestProvider: RuleSetProvider {
|
||||
override fun instance(config: Config): RuleSet {
|
||||
return RuleSet(ruleSetId, listOf(
|
||||
$ruleName(config)
|
||||
))
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `throws an exception`() {
|
||||
assertThatExceptionOfType(InvalidDocumentationException::class.java)
|
||||
.isThrownBy { subject.run(code) }
|
||||
}
|
||||
@Test
|
||||
fun `has one rule`() {
|
||||
val items = subject.run(code)
|
||||
val provider = items[0]
|
||||
assertThat(provider.rules).hasSize(1)
|
||||
assertThat(provider.rules[0]).isEqualTo(ruleName)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `a RuleSetProvider with missing description` {
|
||||
val ruleSetId = "test"
|
||||
val ruleName = "TestRule"
|
||||
val code = """
|
||||
package foo
|
||||
|
||||
class TestProvider: RuleSetProvider {
|
||||
override val ruleSetId: String = "$ruleSetId"
|
||||
|
||||
override fun instance(config: Config): RuleSet {
|
||||
return RuleSet(ruleSetId, listOf(
|
||||
$ruleName(config)
|
||||
))
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `throws an exception`() {
|
||||
assertThatExceptionOfType(InvalidDocumentationException::class.java)
|
||||
.isThrownBy { subject.run(code) }
|
||||
}
|
||||
@Test
|
||||
fun `has correct name`() {
|
||||
val items = subject.run(code)
|
||||
val provider = items[0]
|
||||
assertThat(provider.name).isEqualTo(ruleSetId)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `a RuleSetProvider with invalid activation version` {
|
||||
val code = """
|
||||
package foo
|
||||
|
||||
/**
|
||||
* description
|
||||
*/
|
||||
@ActiveByDefault(since = "1.2.xyz")
|
||||
class TestProvider: RuleSetProvider {
|
||||
override val ruleSetId: String = "ruleSetId"
|
||||
|
||||
override fun instance(config: Config): RuleSet {
|
||||
return RuleSet(ruleSetId, listOf(
|
||||
TestRule(config)
|
||||
))
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `throws an exception`() {
|
||||
assertThatExceptionOfType(InvalidDocumentationException::class.java)
|
||||
.isThrownBy { subject.run(code) }
|
||||
}
|
||||
@Test
|
||||
fun `has correct description`() {
|
||||
val items = subject.run(code)
|
||||
val provider = items[0]
|
||||
assertThat(provider.description).isEqualTo(description)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `a RuleSetProvider with no rules` {
|
||||
val ruleSetId = "test"
|
||||
val code = """
|
||||
package foo
|
||||
@Test
|
||||
fun `is active`() {
|
||||
val items = subject.run(code)
|
||||
val provider = items[0]
|
||||
assertThat(provider.defaultActivationStatus.active).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
class TestProvider: RuleSetProvider {
|
||||
override val ruleSetId: String = "$ruleSetId"
|
||||
@Nested
|
||||
inner class `an inactive RuleSetProvider` {
|
||||
val description = "This is a description"
|
||||
val ruleSetId = "test"
|
||||
val ruleName = "TestRule"
|
||||
val code = """
|
||||
package foo
|
||||
|
||||
override fun instance(config: Config): RuleSet {
|
||||
return RuleSet(ruleSetId, emptyListOf())
|
||||
}
|
||||
}
|
||||
"""
|
||||
/**
|
||||
* $description
|
||||
*/
|
||||
class TestProvider: RuleSetProvider {
|
||||
override val ruleSetId: String = "$ruleSetId"
|
||||
|
||||
@Test
|
||||
fun `throws an exception`() {
|
||||
assertThatExceptionOfType(InvalidDocumentationException::class.java)
|
||||
.isThrownBy { subject.run(code) }
|
||||
override fun instance(config: Config): RuleSet {
|
||||
return RuleSet(ruleSetId, listOf(
|
||||
$ruleName(config)
|
||||
))
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Nested
|
||||
inner class `a correct RuleSetProvider class with full parameters and multiple rules` {
|
||||
val description = "This is a description"
|
||||
val ruleSetId = "test"
|
||||
val ruleName = "TestRule"
|
||||
val secondRuleName = "SecondRule"
|
||||
val code = """
|
||||
package foo
|
||||
@Test
|
||||
fun `is not active`() {
|
||||
val items = subject.run(code)
|
||||
val provider = items[0]
|
||||
assertThat(provider.defaultActivationStatus.active).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* $description
|
||||
*/
|
||||
@ActiveByDefault("1.0.0")
|
||||
class TestProvider: RuleSetProvider {
|
||||
override val ruleSetId: String = "$ruleSetId"
|
||||
@Nested
|
||||
inner class `a RuleSetProvider with missing name` {
|
||||
val description = "This is a description"
|
||||
val ruleName = "TestRule"
|
||||
val code = """
|
||||
package foo
|
||||
|
||||
override fun instance(config: Config): RuleSet {
|
||||
return RuleSet(ruleSetId, listOf(
|
||||
$ruleName(config),
|
||||
$secondRuleName(config)
|
||||
))
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `collects multiple rules`() {
|
||||
val items = subject.run(code)
|
||||
assertThat(items[0].rules).containsExactly(ruleName, secondRuleName)
|
||||
/**
|
||||
* $description
|
||||
*/
|
||||
class TestProvider: RuleSetProvider {
|
||||
override fun instance(config: Config): RuleSet {
|
||||
return RuleSet(ruleSetId, listOf(
|
||||
$ruleName(config)
|
||||
))
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Nested
|
||||
inner class `a RuleSetProvider with configurations in kdoc` {
|
||||
val code = """
|
||||
package foo
|
||||
@Test
|
||||
fun `throws an exception`() {
|
||||
assertThatExceptionOfType(InvalidDocumentationException::class.java)
|
||||
.isThrownBy { subject.run(code) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* description
|
||||
* @configuration android - if android style guides should be preferred (default: `false`)
|
||||
*/
|
||||
class TestProvider: RuleSetProvider {
|
||||
""".trimIndent()
|
||||
@Nested
|
||||
inner class `a RuleSetProvider with missing description` {
|
||||
val ruleSetId = "test"
|
||||
val ruleName = "TestRule"
|
||||
val code = """
|
||||
package foo
|
||||
|
||||
@Test
|
||||
fun `throws exception for configuration in kdoc`() {
|
||||
assertThatExceptionOfType(InvalidDocumentationException::class.java)
|
||||
.isThrownBy { subject.run(code) }
|
||||
class TestProvider: RuleSetProvider {
|
||||
override val ruleSetId: String = "$ruleSetId"
|
||||
|
||||
override fun instance(config: Config): RuleSet {
|
||||
return RuleSet(ruleSetId, listOf(
|
||||
$ruleName(config)
|
||||
))
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Nested
|
||||
inner class `a RuleSetProvider with configurations` {
|
||||
val code = """
|
||||
package foo
|
||||
@Test
|
||||
fun `throws an exception`() {
|
||||
assertThatExceptionOfType(InvalidDocumentationException::class.java)
|
||||
.isThrownBy { subject.run(code) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* description
|
||||
*/
|
||||
class TestProvider: RuleSetProvider {
|
||||
override val ruleSetId: String = "ruleSetId"
|
||||
@Nested
|
||||
inner class `a RuleSetProvider with invalid activation version` {
|
||||
val code = """
|
||||
package foo
|
||||
|
||||
override fun instance(config: Config): RuleSet {
|
||||
return RuleSet(ruleSetId, listOf(RruleName(config)))
|
||||
}
|
||||
/**
|
||||
* description
|
||||
*/
|
||||
@ActiveByDefault(since = "1.2.xyz")
|
||||
class TestProvider: RuleSetProvider {
|
||||
override val ruleSetId: String = "ruleSetId"
|
||||
|
||||
companion object {
|
||||
@Configuration("bool description")
|
||||
val aBool by ruleSetConfig(true)
|
||||
|
||||
@Configuration("int description")
|
||||
val anInt by ruleSetConfig(99)
|
||||
|
||||
@Deprecated("use something else")
|
||||
@Configuration("string description")
|
||||
val aString by ruleSetConfig("a")
|
||||
}
|
||||
}
|
||||
"""
|
||||
private val items = subject.run(code)
|
||||
|
||||
@Test
|
||||
fun `extracts boolean configuration option`() {
|
||||
val conf = items[0].configuration[0]
|
||||
assertThat(conf.name).isEqualTo("aBool")
|
||||
assertThat(conf.description).isEqualTo("bool description")
|
||||
assertThat(conf.defaultValue).isEqualTo(of(true))
|
||||
assertThat(conf.deprecated).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extracts int configuration option`() {
|
||||
val conf = items[0].configuration[1]
|
||||
assertThat(conf.name).isEqualTo("anInt")
|
||||
assertThat(conf.description).isEqualTo("int description")
|
||||
assertThat(conf.defaultValue).isEqualTo(of(99))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extracts string configuration option`() {
|
||||
val conf = items[0].configuration[2]
|
||||
assertThat(conf.name).isEqualTo("aString")
|
||||
assertThat(conf.description).isEqualTo("string description")
|
||||
assertThat(conf.defaultValue).isEqualTo(of("a"))
|
||||
assertThat(conf.deprecated).isEqualTo("use something else")
|
||||
override fun instance(config: Config): RuleSet {
|
||||
return RuleSet(ruleSetId, listOf(
|
||||
TestRule(config)
|
||||
))
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Nested
|
||||
inner class `a RuleSetProvider with unsupported configuration format` {
|
||||
val code = """
|
||||
package foo
|
||||
@Test
|
||||
fun `throws an exception`() {
|
||||
assertThatExceptionOfType(InvalidDocumentationException::class.java)
|
||||
.isThrownBy { subject.run(code) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* description
|
||||
*/
|
||||
class TestProvider: RuleSetProvider {
|
||||
override val ruleSetId: String = "ruleSetId"
|
||||
@Nested
|
||||
inner class `a RuleSetProvider with no rules` {
|
||||
val ruleSetId = "test"
|
||||
val code = """
|
||||
package foo
|
||||
|
||||
override fun instance(config: Config): RuleSet {
|
||||
return RuleSet(ruleSetId, listOf(RruleName(config)))
|
||||
}
|
||||
class TestProvider: RuleSetProvider {
|
||||
override val ruleSetId: String = "$ruleSetId"
|
||||
|
||||
companion object {
|
||||
@Configuration("a description")
|
||||
val aConfig by ruleSetConfig(listOf("a"))
|
||||
}
|
||||
override fun instance(config: Config): RuleSet {
|
||||
return RuleSet(ruleSetId, emptyListOf())
|
||||
}
|
||||
"""
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `fails`() {
|
||||
assertThatThrownBy { subject.run(code) }
|
||||
.isInstanceOf(InvalidDocumentationException::class.java)
|
||||
.hasMessageContaining("""Unsupported default value format 'listOf("a")'""")
|
||||
@Test
|
||||
fun `throws an exception`() {
|
||||
assertThatExceptionOfType(InvalidDocumentationException::class.java)
|
||||
.isThrownBy { subject.run(code) }
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `a correct RuleSetProvider class with full parameters and multiple rules` {
|
||||
val description = "This is a description"
|
||||
val ruleSetId = "test"
|
||||
val ruleName = "TestRule"
|
||||
val secondRuleName = "SecondRule"
|
||||
val code = """
|
||||
package foo
|
||||
|
||||
/**
|
||||
* $description
|
||||
*/
|
||||
@ActiveByDefault("1.0.0")
|
||||
class TestProvider: RuleSetProvider {
|
||||
override val ruleSetId: String = "$ruleSetId"
|
||||
|
||||
override fun instance(config: Config): RuleSet {
|
||||
return RuleSet(ruleSetId, listOf(
|
||||
$ruleName(config),
|
||||
$secondRuleName(config)
|
||||
))
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `collects multiple rules`() {
|
||||
val items = subject.run(code)
|
||||
assertThat(items[0].rules).containsExactly(ruleName, secondRuleName)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `a RuleSetProvider with configurations in kdoc` {
|
||||
val code = """
|
||||
package foo
|
||||
|
||||
/**
|
||||
* description
|
||||
* @configuration android - if android style guides should be preferred (default: `false`)
|
||||
*/
|
||||
class TestProvider: RuleSetProvider {
|
||||
""".trimIndent()
|
||||
|
||||
@Test
|
||||
fun `throws exception for configuration in kdoc`() {
|
||||
assertThatExceptionOfType(InvalidDocumentationException::class.java)
|
||||
.isThrownBy { subject.run(code) }
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `a RuleSetProvider with configurations` {
|
||||
val code = """
|
||||
package foo
|
||||
|
||||
/**
|
||||
* description
|
||||
*/
|
||||
class TestProvider: RuleSetProvider {
|
||||
override val ruleSetId: String = "ruleSetId"
|
||||
|
||||
override fun instance(config: Config): RuleSet {
|
||||
return RuleSet(ruleSetId, listOf(RruleName(config)))
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Configuration("bool description")
|
||||
val aBool by ruleSetConfig(true)
|
||||
|
||||
@Configuration("int description")
|
||||
val anInt by ruleSetConfig(99)
|
||||
|
||||
@Deprecated("use something else")
|
||||
@Configuration("string description")
|
||||
val aString by ruleSetConfig("a")
|
||||
}
|
||||
}
|
||||
"""
|
||||
private val items = subject.run(code)
|
||||
|
||||
@Test
|
||||
fun `extracts boolean configuration option`() {
|
||||
val conf = items[0].configuration[0]
|
||||
assertThat(conf.name).isEqualTo("aBool")
|
||||
assertThat(conf.description).isEqualTo("bool description")
|
||||
assertThat(conf.defaultValue).isEqualTo(of(true))
|
||||
assertThat(conf.deprecated).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extracts int configuration option`() {
|
||||
val conf = items[0].configuration[1]
|
||||
assertThat(conf.name).isEqualTo("anInt")
|
||||
assertThat(conf.description).isEqualTo("int description")
|
||||
assertThat(conf.defaultValue).isEqualTo(of(99))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extracts string configuration option`() {
|
||||
val conf = items[0].configuration[2]
|
||||
assertThat(conf.name).isEqualTo("aString")
|
||||
assertThat(conf.description).isEqualTo("string description")
|
||||
assertThat(conf.defaultValue).isEqualTo(of("a"))
|
||||
assertThat(conf.deprecated).isEqualTo("use something else")
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `a RuleSetProvider with unsupported configuration format` {
|
||||
val code = """
|
||||
package foo
|
||||
|
||||
/**
|
||||
* description
|
||||
*/
|
||||
class TestProvider: RuleSetProvider {
|
||||
override val ruleSetId: String = "ruleSetId"
|
||||
|
||||
override fun instance(config: Config): RuleSet {
|
||||
return RuleSet(ruleSetId, listOf(RruleName(config)))
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Configuration("a description")
|
||||
val aConfig by ruleSetConfig(listOf("a"))
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `fails`() {
|
||||
assertThatThrownBy { subject.run(code) }
|
||||
.isInstanceOf(InvalidDocumentationException::class.java)
|
||||
.hasMessageContaining("""Unsupported default value format 'listOf("a")'""")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,22 +2,17 @@ package io.gitlab.arturbosch.detekt.generator.printer
|
||||
|
||||
import io.github.detekt.test.utils.createTempFileForTest
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class CliOptionsPrinterSpec {
|
||||
|
||||
@Nested
|
||||
inner class `Cli Options Printer` {
|
||||
@Test
|
||||
fun `prints the correct cli-options_md`() {
|
||||
val cliOptionsFile = createTempFileForTest("cli-options", ".md")
|
||||
CliOptionsPrinter().print(cliOptionsFile.toAbsolutePath())
|
||||
val markdownString = cliOptionsFile.toFile().readText()
|
||||
|
||||
@Test
|
||||
fun `prints the correct cli-options_md`() {
|
||||
val cliOptionsFile = createTempFileForTest("cli-options", ".md")
|
||||
CliOptionsPrinter().print(cliOptionsFile.toAbsolutePath())
|
||||
val markdownString = cliOptionsFile.toFile().readText()
|
||||
|
||||
assertThat(markdownString).contains("Usage: detekt [options]")
|
||||
assertThat(markdownString).contains("--input, -i")
|
||||
}
|
||||
assertThat(markdownString).contains("Usage: detekt [options]")
|
||||
assertThat(markdownString).contains("--input, -i")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,22 +2,18 @@ package io.gitlab.arturbosch.detekt.generator.printer
|
||||
|
||||
import io.gitlab.arturbosch.detekt.generator.util.createRuleSetPage
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class DeprecatedPrinterSpec {
|
||||
|
||||
@Nested
|
||||
inner class `Deprecated page printer` {
|
||||
@Test
|
||||
fun `prints the correct properties`() {
|
||||
val markdownString = DeprecatedPrinter.print(listOf(createRuleSetPage()))
|
||||
val expectedMarkdownString = """
|
||||
style>WildcardImport>conf2=use conf1 instead
|
||||
style>WildcardImport>conf4=use conf3 instead
|
||||
|
||||
""".trimIndent()
|
||||
assertThat(markdownString).isEqualTo(expectedMarkdownString)
|
||||
}
|
||||
@Test
|
||||
fun `prints the correct properties`() {
|
||||
val markdownString = DeprecatedPrinter.print(listOf(createRuleSetPage()))
|
||||
val expectedMarkdownString = """
|
||||
style>WildcardImport>conf2=use conf1 instead
|
||||
style>WildcardImport>conf4=use conf3 instead
|
||||
|
||||
""".trimIndent()
|
||||
assertThat(markdownString).isEqualTo(expectedMarkdownString)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,54 +3,50 @@ package io.gitlab.arturbosch.detekt.generator.printer.defaultconfig
|
||||
import io.github.detekt.test.utils.readResourceContent
|
||||
import io.gitlab.arturbosch.detekt.generator.util.createRuleSetPage
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class ConfigPrinterSpec {
|
||||
|
||||
@Nested
|
||||
inner class `Config printer` {
|
||||
val ruleSetPage = createRuleSetPage()
|
||||
val yamlString = ConfigPrinter.print(listOf(ruleSetPage))
|
||||
private val ruleSetPage = createRuleSetPage()
|
||||
private val yamlString = ConfigPrinter.print(listOf(ruleSetPage))
|
||||
|
||||
@Test
|
||||
fun `prints the rule set in the correct yaml format`() {
|
||||
val expectedRulePart = readResourceContent("RuleSetConfig.yml")
|
||||
@Test
|
||||
fun `prints the rule set in the correct yaml format`() {
|
||||
val expectedRulePart = readResourceContent("RuleSetConfig.yml")
|
||||
|
||||
assertThat(yamlString).contains(expectedRulePart)
|
||||
}
|
||||
assertThat(yamlString).contains(expectedRulePart)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `prints default build configuration`() {
|
||||
assertThat(yamlString).contains("build:")
|
||||
}
|
||||
@Test
|
||||
fun `prints default build configuration`() {
|
||||
assertThat(yamlString).contains("build:")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `prints default config configuration`() {
|
||||
assertThat(yamlString).contains("config:")
|
||||
}
|
||||
@Test
|
||||
fun `prints default config configuration`() {
|
||||
assertThat(yamlString).contains("config:")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `prints default processor configuration`() {
|
||||
assertThat(yamlString).contains("processors:")
|
||||
}
|
||||
@Test
|
||||
fun `prints default processor configuration`() {
|
||||
assertThat(yamlString).contains("processors:")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `prints default report configuration`() {
|
||||
assertThat(yamlString).contains("output-reports:")
|
||||
assertThat(yamlString).contains("console-reports:")
|
||||
}
|
||||
@Test
|
||||
fun `prints default report configuration`() {
|
||||
assertThat(yamlString).contains("output-reports:")
|
||||
assertThat(yamlString).contains("console-reports:")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `omits deprecated ruleset properties`() {
|
||||
assertThat(yamlString).doesNotContain("deprecatedSimpleConfig")
|
||||
assertThat(yamlString).doesNotContain("deprecatedListConfig")
|
||||
}
|
||||
@Test
|
||||
fun `omits deprecated ruleset properties`() {
|
||||
assertThat(yamlString).doesNotContain("deprecatedSimpleConfig")
|
||||
assertThat(yamlString).doesNotContain("deprecatedListConfig")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `omits deprecated rule properties`() {
|
||||
assertThat(yamlString).doesNotContain("conf2")
|
||||
assertThat(yamlString).doesNotContain("conf4")
|
||||
}
|
||||
@Test
|
||||
fun `omits deprecated rule properties`() {
|
||||
assertThat(yamlString).doesNotContain("conf2")
|
||||
assertThat(yamlString).doesNotContain("conf4")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,417 +10,414 @@ import org.junit.jupiter.api.condition.EnabledForJreRange
|
||||
import org.junit.jupiter.api.condition.EnabledIf
|
||||
import org.junit.jupiter.api.condition.JRE
|
||||
|
||||
@EnabledForJreRange(min = JRE.JAVA_11, disabledReason = "Android Gradle Plugin 7.0+ requires JDK 11 or newer")
|
||||
@EnabledIf("io.gitlab.arturbosch.detekt.DetektAndroidSpecKt#isAndroidSdkInstalled")
|
||||
class DetektAndroidSpec {
|
||||
|
||||
@Nested
|
||||
@EnabledForJreRange(min = JRE.JAVA_11, disabledReason = "Android Gradle Plugin 7.0+ requires JDK 11 or newer")
|
||||
@EnabledIf("io.gitlab.arturbosch.detekt.DetektAndroidSpecKt#isAndroidSdkInstalled")
|
||||
inner class `When applying detekt in an Android project` {
|
||||
inner class `configures android tasks for android application` {
|
||||
val projectLayout = ProjectLayout(
|
||||
numberOfSourceFilesInRootPerSourceDir = 0,
|
||||
).apply {
|
||||
addSubmodule(
|
||||
name = "app",
|
||||
numberOfSourceFilesPerSourceDir = 1,
|
||||
numberOfCodeSmells = 1,
|
||||
buildFileContent = """
|
||||
$APP_PLUGIN_BLOCK
|
||||
$ANDROID_BLOCK
|
||||
$DETEKT_REPORTS_BLOCK
|
||||
""".trimIndent(),
|
||||
srcDirs = listOf(
|
||||
"src/main/java",
|
||||
"src/debug/java",
|
||||
"src/test/java",
|
||||
"src/androidTest/java",
|
||||
"src/main/kotlin",
|
||||
"src/debug/kotlin",
|
||||
"src/test/kotlin",
|
||||
"src/androidTest/kotlin",
|
||||
),
|
||||
baselineFiles = listOf(
|
||||
"detekt-baseline.xml",
|
||||
"detekt-baseline-release.xml",
|
||||
"detekt-baseline-debug.xml",
|
||||
"detekt-baseline-releaseUnitTest.xml",
|
||||
"detekt-baseline-debugUnitTest.xml",
|
||||
"detekt-baseline-debugAndroidTest.xml"
|
||||
)
|
||||
)
|
||||
}
|
||||
val gradleRunner = createGradleRunnerAndSetupProject(projectLayout).also {
|
||||
it.writeProjectFile("app/src/main/AndroidManifest.xml", manifestContent())
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `configures android tasks for android application` {
|
||||
val projectLayout = ProjectLayout(
|
||||
numberOfSourceFilesInRootPerSourceDir = 0,
|
||||
).apply {
|
||||
addSubmodule(
|
||||
name = "app",
|
||||
numberOfSourceFilesPerSourceDir = 1,
|
||||
numberOfCodeSmells = 1,
|
||||
buildFileContent = """
|
||||
$APP_PLUGIN_BLOCK
|
||||
$ANDROID_BLOCK
|
||||
$DETEKT_REPORTS_BLOCK
|
||||
""".trimIndent(),
|
||||
srcDirs = listOf(
|
||||
"src/main/java",
|
||||
"src/debug/java",
|
||||
"src/test/java",
|
||||
"src/androidTest/java",
|
||||
"src/main/kotlin",
|
||||
"src/debug/kotlin",
|
||||
"src/test/kotlin",
|
||||
"src/androidTest/kotlin",
|
||||
),
|
||||
baselineFiles = listOf(
|
||||
"detekt-baseline.xml",
|
||||
"detekt-baseline-release.xml",
|
||||
"detekt-baseline-debug.xml",
|
||||
"detekt-baseline-releaseUnitTest.xml",
|
||||
"detekt-baseline-debugUnitTest.xml",
|
||||
"detekt-baseline-debugAndroidTest.xml"
|
||||
@Test
|
||||
@DisplayName("task :app:detektMain")
|
||||
fun appDetektMain() {
|
||||
gradleRunner.runTasksAndCheckResult(":app:detektMain") { buildResult ->
|
||||
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-release.xml """)
|
||||
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-debug.xml """)
|
||||
assertThat(buildResult.output).containsPattern("""--input \S*[/\\]app[/\\]src[/\\]main[/\\]java""")
|
||||
assertThat(buildResult.output).containsPattern("""--input \S*[/\\]app[/\\]src[/\\]debug[/\\]java""")
|
||||
assertThat(buildResult.output).containsPattern("""--input \S*[/\\]app[/\\]src[/\\]main[/\\]kotlin""")
|
||||
assertThat(buildResult.output).containsPattern("""--input \S*[/\\]app[/\\]src[/\\]debug[/\\]kotlin""")
|
||||
assertThat(buildResult.output).contains("--report xml:")
|
||||
assertThat(buildResult.output).contains("--report sarif:")
|
||||
assertThat(buildResult.output).doesNotContain("--report txt:")
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":app:detektMain",
|
||||
":app:detektDebug"
|
||||
)
|
||||
)
|
||||
}
|
||||
val gradleRunner = createGradleRunnerAndSetupProject(projectLayout).also {
|
||||
it.writeProjectFile("app/src/main/AndroidManifest.xml", manifestContent())
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :app:detektMain")
|
||||
fun appDetektMain() {
|
||||
gradleRunner.runTasksAndCheckResult(":app:detektMain") { buildResult ->
|
||||
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-release.xml """)
|
||||
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-debug.xml """)
|
||||
assertThat(buildResult.output).containsPattern("""--input \S*[/\\]app[/\\]src[/\\]main[/\\]java""")
|
||||
assertThat(buildResult.output).containsPattern("""--input \S*[/\\]app[/\\]src[/\\]debug[/\\]java""")
|
||||
assertThat(buildResult.output).containsPattern("""--input \S*[/\\]app[/\\]src[/\\]main[/\\]kotlin""")
|
||||
assertThat(buildResult.output).containsPattern("""--input \S*[/\\]app[/\\]src[/\\]debug[/\\]kotlin""")
|
||||
assertThat(buildResult.output).contains("--report xml:")
|
||||
assertThat(buildResult.output).contains("--report sarif:")
|
||||
assertThat(buildResult.output).doesNotContain("--report txt:")
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":app:detektMain",
|
||||
":app:detektDebug"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :app:detektTest")
|
||||
fun appDetektTest() {
|
||||
gradleRunner.runTasksAndCheckResult(":app:detektTest") { buildResult ->
|
||||
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-releaseUnitTest.xml """)
|
||||
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-debugUnitTest.xml """)
|
||||
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-debugAndroidTest.xml """)
|
||||
assertThat(buildResult.output).containsPattern("""--input \S*[/\\]app[/\\]src[/\\]test[/\\]java""")
|
||||
assertThat(buildResult.output).containsPattern("""--input \S*[/\\]app[/\\]src[/\\]androidTest[/\\]java""")
|
||||
assertThat(buildResult.output).containsPattern("""--input \S*[/\\]app[/\\]src[/\\]test[/\\]kotlin""")
|
||||
assertThat(buildResult.output).containsPattern("""--input \S*[/\\]app[/\\]src[/\\]androidTest[/\\]kotlin""")
|
||||
assertThat(buildResult.output).contains("--report xml:")
|
||||
assertThat(buildResult.output).contains("--report sarif:")
|
||||
assertThat(buildResult.output).doesNotContain("--report txt:")
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":app:detektDebugUnitTest",
|
||||
":app:detektDebugAndroidTest"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `does not configures android tasks if user opts out` {
|
||||
val projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 0).apply {
|
||||
addSubmodule(
|
||||
name = "app",
|
||||
numberOfSourceFilesPerSourceDir = 1,
|
||||
numberOfCodeSmells = 1,
|
||||
buildFileContent = """
|
||||
$APP_PLUGIN_BLOCK
|
||||
$ANDROID_BLOCK
|
||||
$DETEKT_REPORTS_BLOCK
|
||||
""".trimIndent(),
|
||||
srcDirs = listOf("src/main/java", "src/debug/java", "src/test/java", "src/androidTest/java")
|
||||
@Test
|
||||
@DisplayName("task :app:detektTest")
|
||||
fun appDetektTest() {
|
||||
gradleRunner.runTasksAndCheckResult(":app:detektTest") { buildResult ->
|
||||
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-releaseUnitTest.xml """)
|
||||
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-debugUnitTest.xml """)
|
||||
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-debugAndroidTest.xml """)
|
||||
assertThat(buildResult.output).containsPattern("""--input \S*[/\\]app[/\\]src[/\\]test[/\\]java""")
|
||||
assertThat(buildResult.output).containsPattern("""--input \S*[/\\]app[/\\]src[/\\]androidTest[/\\]java""")
|
||||
assertThat(buildResult.output).containsPattern("""--input \S*[/\\]app[/\\]src[/\\]test[/\\]kotlin""")
|
||||
assertThat(buildResult.output).containsPattern("""--input \S*[/\\]app[/\\]src[/\\]androidTest[/\\]kotlin""")
|
||||
assertThat(buildResult.output).contains("--report xml:")
|
||||
assertThat(buildResult.output).contains("--report sarif:")
|
||||
assertThat(buildResult.output).doesNotContain("--report txt:")
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":app:detektDebugUnitTest",
|
||||
":app:detektDebugAndroidTest"
|
||||
)
|
||||
)
|
||||
}
|
||||
val gradleRunner = createGradleRunnerAndSetupProject(projectLayout).also {
|
||||
it.writeProjectFile("gradle.properties", "detekt.android.disabled=true")
|
||||
it.writeProjectFile("app/src/main/AndroidManifest.xml", manifestContent())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :app:detekt")
|
||||
fun appDetekt() {
|
||||
gradleRunner.runTasks(":app:detekt")
|
||||
}
|
||||
@Nested
|
||||
inner class `does not configures android tasks if user opts out` {
|
||||
val projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 0).apply {
|
||||
addSubmodule(
|
||||
name = "app",
|
||||
numberOfSourceFilesPerSourceDir = 1,
|
||||
numberOfCodeSmells = 1,
|
||||
buildFileContent = """
|
||||
$APP_PLUGIN_BLOCK
|
||||
$ANDROID_BLOCK
|
||||
$DETEKT_REPORTS_BLOCK
|
||||
""".trimIndent(),
|
||||
srcDirs = listOf("src/main/java", "src/debug/java", "src/test/java", "src/androidTest/java")
|
||||
)
|
||||
}
|
||||
val gradleRunner = createGradleRunnerAndSetupProject(projectLayout).also {
|
||||
it.writeProjectFile("gradle.properties", "detekt.android.disabled=true")
|
||||
it.writeProjectFile("app/src/main/AndroidManifest.xml", manifestContent())
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :app:detektMain")
|
||||
fun appDetektMain() {
|
||||
gradleRunner.runTasksAndExpectFailure(":app:detektMain") { result ->
|
||||
assertThat(result.output).contains("Task 'detektMain' not found in project")
|
||||
}
|
||||
}
|
||||
@Test
|
||||
@DisplayName("task :app:detekt")
|
||||
fun appDetekt() {
|
||||
gradleRunner.runTasks(":app:detekt")
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :app:detektTest")
|
||||
fun appDetektTest() {
|
||||
gradleRunner.runTasksAndExpectFailure(":app:detektTest") { result ->
|
||||
assertThat(result.output).contains("Task 'detektTest' not found in project")
|
||||
}
|
||||
@Test
|
||||
@DisplayName("task :app:detektMain")
|
||||
fun appDetektMain() {
|
||||
gradleRunner.runTasksAndExpectFailure(":app:detektMain") { result ->
|
||||
assertThat(result.output).contains("Task 'detektMain' not found in project")
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `configures android tasks for android library` {
|
||||
val projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 0).apply {
|
||||
addSubmodule(
|
||||
name = "lib",
|
||||
numberOfSourceFilesPerSourceDir = 1,
|
||||
numberOfCodeSmells = 1,
|
||||
buildFileContent = """
|
||||
$LIB_PLUGIN_BLOCK
|
||||
$ANDROID_BLOCK
|
||||
$DETEKT_REPORTS_BLOCK
|
||||
""".trimIndent(),
|
||||
srcDirs = listOf("src/main/java", "src/debug/java", "src/test/java", "src/androidTest/java"),
|
||||
baselineFiles = listOf(
|
||||
"detekt-baseline.xml",
|
||||
"detekt-baseline-release.xml",
|
||||
"detekt-baseline-debug.xml",
|
||||
"detekt-baseline-releaseUnitTest.xml",
|
||||
"detekt-baseline-debugUnitTest.xml",
|
||||
"detekt-baseline-debugAndroidTest.xml"
|
||||
@Test
|
||||
@DisplayName("task :app:detektTest")
|
||||
fun appDetektTest() {
|
||||
gradleRunner.runTasksAndExpectFailure(":app:detektTest") { result ->
|
||||
assertThat(result.output).contains("Task 'detektTest' not found in project")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `configures android tasks for android library` {
|
||||
val projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 0).apply {
|
||||
addSubmodule(
|
||||
name = "lib",
|
||||
numberOfSourceFilesPerSourceDir = 1,
|
||||
numberOfCodeSmells = 1,
|
||||
buildFileContent = """
|
||||
$LIB_PLUGIN_BLOCK
|
||||
$ANDROID_BLOCK
|
||||
$DETEKT_REPORTS_BLOCK
|
||||
""".trimIndent(),
|
||||
srcDirs = listOf("src/main/java", "src/debug/java", "src/test/java", "src/androidTest/java"),
|
||||
baselineFiles = listOf(
|
||||
"detekt-baseline.xml",
|
||||
"detekt-baseline-release.xml",
|
||||
"detekt-baseline-debug.xml",
|
||||
"detekt-baseline-releaseUnitTest.xml",
|
||||
"detekt-baseline-debugUnitTest.xml",
|
||||
"detekt-baseline-debugAndroidTest.xml"
|
||||
)
|
||||
)
|
||||
}
|
||||
val gradleRunner = createGradleRunnerAndSetupProject(projectLayout).also {
|
||||
it.writeProjectFile("lib/src/main/AndroidManifest.xml", manifestContent())
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :lib:detektMain")
|
||||
fun libDetektMain() {
|
||||
gradleRunner.runTasksAndCheckResult(":lib:detektMain") { buildResult ->
|
||||
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-release.xml """)
|
||||
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-debug.xml """)
|
||||
assertThat(buildResult.output).contains("--report xml:")
|
||||
assertThat(buildResult.output).contains("--report sarif:")
|
||||
assertThat(buildResult.output).doesNotContain("--report txt:")
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":lib:detektMain",
|
||||
":lib:detektDebug"
|
||||
)
|
||||
)
|
||||
}
|
||||
val gradleRunner = createGradleRunnerAndSetupProject(projectLayout).also {
|
||||
it.writeProjectFile("lib/src/main/AndroidManifest.xml", manifestContent())
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :lib:detektMain")
|
||||
fun libDetektMain() {
|
||||
gradleRunner.runTasksAndCheckResult(":lib:detektMain") { buildResult ->
|
||||
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-release.xml """)
|
||||
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-debug.xml """)
|
||||
assertThat(buildResult.output).contains("--report xml:")
|
||||
assertThat(buildResult.output).contains("--report sarif:")
|
||||
assertThat(buildResult.output).doesNotContain("--report txt:")
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":lib:detektMain",
|
||||
":lib:detektDebug"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :lib:detektTest")
|
||||
fun libDetektTest() {
|
||||
gradleRunner.runTasksAndCheckResult(":lib:detektTest") { buildResult ->
|
||||
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-releaseUnitTest.xml """)
|
||||
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-debugUnitTest.xml """)
|
||||
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-debugAndroidTest.xml """)
|
||||
assertThat(buildResult.output).contains("--report xml:")
|
||||
assertThat(buildResult.output).contains("--report sarif:")
|
||||
assertThat(buildResult.output).doesNotContain("--report txt:")
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":lib:detektDebugUnitTest",
|
||||
":lib:detektDebugAndroidTest"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `configures android tasks for different build variants` {
|
||||
|
||||
val projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 0).apply {
|
||||
addSubmodule(
|
||||
name = "lib",
|
||||
numberOfSourceFilesPerSourceDir = 1,
|
||||
numberOfCodeSmells = 1,
|
||||
buildFileContent = """
|
||||
$LIB_PLUGIN_BLOCK
|
||||
$ANDROID_BLOCK_WITH_FLAVOR
|
||||
$DETEKT_REPORTS_BLOCK
|
||||
""".trimIndent(),
|
||||
srcDirs = listOf("src/main/java", "src/debug/java", "src/test/java", "src/androidTest/java")
|
||||
@Test
|
||||
@DisplayName("task :lib:detektTest")
|
||||
fun libDetektTest() {
|
||||
gradleRunner.runTasksAndCheckResult(":lib:detektTest") { buildResult ->
|
||||
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-releaseUnitTest.xml """)
|
||||
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-debugUnitTest.xml """)
|
||||
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-debugAndroidTest.xml """)
|
||||
assertThat(buildResult.output).contains("--report xml:")
|
||||
assertThat(buildResult.output).contains("--report sarif:")
|
||||
assertThat(buildResult.output).doesNotContain("--report txt:")
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":lib:detektDebugUnitTest",
|
||||
":lib:detektDebugAndroidTest"
|
||||
)
|
||||
)
|
||||
}
|
||||
val gradleRunner = createGradleRunnerAndSetupProject(projectLayout).also {
|
||||
it.writeProjectFile("lib/src/main/AndroidManifest.xml", manifestContent())
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :lib:detektMain")
|
||||
fun libDetektMain() {
|
||||
gradleRunner.runTasksAndCheckResult(":lib:detektMain") { buildResult ->
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":lib:detektYoungHarryDebug",
|
||||
":lib:detektOldHarryDebug",
|
||||
":lib:detektOldHarryRelease"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :lib:detektTest")
|
||||
fun libDetektTest() {
|
||||
gradleRunner.runTasksAndCheckResult(":lib:detektTest") { buildResult ->
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":lib:detektYoungHarryDebugUnitTest",
|
||||
":lib:detektOldHarryDebugUnitTest",
|
||||
":lib:detektOldHarryReleaseUnitTest",
|
||||
":lib:detektYoungHarryDebugAndroidTest",
|
||||
":lib:detektOldHarryDebugAndroidTest"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `configures android tasks for different build variants excluding ignored build types` {
|
||||
@Nested
|
||||
inner class `configures android tasks for different build variants` {
|
||||
|
||||
val projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 0).apply {
|
||||
addSubmodule(
|
||||
name = "lib",
|
||||
numberOfSourceFilesPerSourceDir = 1,
|
||||
numberOfCodeSmells = 1,
|
||||
buildFileContent = """
|
||||
$LIB_PLUGIN_BLOCK
|
||||
$ANDROID_BLOCK_WITH_FLAVOR
|
||||
detekt {
|
||||
ignoredBuildTypes = listOf("release")
|
||||
}
|
||||
""".trimIndent(),
|
||||
srcDirs = listOf("src/main/java", "src/debug/java", "src/test/java", "src/androidTest/java")
|
||||
)
|
||||
}
|
||||
val gradleRunner = createGradleRunnerAndSetupProject(projectLayout).also {
|
||||
it.writeProjectFile("lib/src/main/AndroidManifest.xml", manifestContent())
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :lib:detektMain")
|
||||
fun libDetektMain() {
|
||||
gradleRunner.runTasksAndCheckResult(":lib:detektMain") { buildResult ->
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":lib:detektYoungHarryDebug",
|
||||
":lib:detektOldHarryDebug"
|
||||
)
|
||||
).doesNotContain(
|
||||
":lib:detektOldHarryRelease"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :lib:detektTest")
|
||||
fun libDetektTest() {
|
||||
gradleRunner.runTasksAndCheckResult(":lib:detektTest") { buildResult ->
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":lib:detektYoungHarryDebugUnitTest",
|
||||
":lib:detektOldHarryDebugUnitTest",
|
||||
":lib:detektYoungHarryDebugAndroidTest",
|
||||
":lib:detektOldHarryDebugAndroidTest"
|
||||
)
|
||||
).doesNotContain(
|
||||
":lib:detektOldHarryReleaseUnitTest"
|
||||
)
|
||||
}
|
||||
}
|
||||
val projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 0).apply {
|
||||
addSubmodule(
|
||||
name = "lib",
|
||||
numberOfSourceFilesPerSourceDir = 1,
|
||||
numberOfCodeSmells = 1,
|
||||
buildFileContent = """
|
||||
$LIB_PLUGIN_BLOCK
|
||||
$ANDROID_BLOCK_WITH_FLAVOR
|
||||
$DETEKT_REPORTS_BLOCK
|
||||
""".trimIndent(),
|
||||
srcDirs = listOf("src/main/java", "src/debug/java", "src/test/java", "src/androidTest/java")
|
||||
)
|
||||
}
|
||||
val gradleRunner = createGradleRunnerAndSetupProject(projectLayout).also {
|
||||
it.writeProjectFile("lib/src/main/AndroidManifest.xml", manifestContent())
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `configures android tasks for different build variants excluding ignored variants` {
|
||||
|
||||
val projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 0).apply {
|
||||
addSubmodule(
|
||||
name = "lib",
|
||||
numberOfSourceFilesPerSourceDir = 1,
|
||||
numberOfCodeSmells = 1,
|
||||
buildFileContent = """
|
||||
$LIB_PLUGIN_BLOCK
|
||||
$ANDROID_BLOCK_WITH_FLAVOR
|
||||
detekt {
|
||||
ignoredVariants = listOf("youngHarryDebug", "oldHarryRelease")
|
||||
}
|
||||
""".trimIndent(),
|
||||
srcDirs = listOf("src/main/java", "src/debug/java", "src/test/java", "src/androidTest/java")
|
||||
)
|
||||
}
|
||||
val gradleRunner = createGradleRunnerAndSetupProject(projectLayout).also {
|
||||
it.writeProjectFile("lib/src/main/AndroidManifest.xml", manifestContent())
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :lib:detektMain")
|
||||
fun libDetektMain() {
|
||||
gradleRunner.runTasksAndCheckResult(":lib:detektMain") { buildResult ->
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":lib:detektOldHarryDebug"
|
||||
)
|
||||
).doesNotContain(
|
||||
@Test
|
||||
@DisplayName("task :lib:detektMain")
|
||||
fun libDetektMain() {
|
||||
gradleRunner.runTasksAndCheckResult(":lib:detektMain") { buildResult ->
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":lib:detektYoungHarryDebug",
|
||||
":lib:detektOldHarryDebug",
|
||||
":lib:detektOldHarryRelease"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :lib:detektTest")
|
||||
fun libDetektTest() {
|
||||
gradleRunner.runTasksAndCheckResult(":lib:detektTest") { buildResult ->
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":lib:detektOldHarryDebugUnitTest",
|
||||
":lib:detektOldHarryDebugAndroidTest"
|
||||
)
|
||||
).doesNotContain(
|
||||
":lib:detektYoungHarryDebugUnitTest",
|
||||
":lib:detektYoungHarryDebugAndroidTest",
|
||||
":lib:detektOldHarryReleaseUnitTest"
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `configures android tasks for different build variants excluding ignored flavors` {
|
||||
|
||||
val projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 0).apply {
|
||||
addSubmodule(
|
||||
name = "lib",
|
||||
numberOfSourceFilesPerSourceDir = 1,
|
||||
numberOfCodeSmells = 1,
|
||||
buildFileContent = """
|
||||
$LIB_PLUGIN_BLOCK
|
||||
$ANDROID_BLOCK_WITH_FLAVOR
|
||||
detekt {
|
||||
ignoredFlavors = listOf("youngHarry")
|
||||
}
|
||||
""".trimIndent(),
|
||||
srcDirs = listOf("src/main/java", "src/debug/java", "src/test/java", "src/androidTest/java")
|
||||
@Test
|
||||
@DisplayName("task :lib:detektTest")
|
||||
fun libDetektTest() {
|
||||
gradleRunner.runTasksAndCheckResult(":lib:detektTest") { buildResult ->
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":lib:detektYoungHarryDebugUnitTest",
|
||||
":lib:detektOldHarryDebugUnitTest",
|
||||
":lib:detektOldHarryReleaseUnitTest",
|
||||
":lib:detektYoungHarryDebugAndroidTest",
|
||||
":lib:detektOldHarryDebugAndroidTest"
|
||||
)
|
||||
)
|
||||
}
|
||||
val gradleRunner = createGradleRunnerAndSetupProject(projectLayout).also {
|
||||
it.writeProjectFile("lib/src/main/AndroidManifest.xml", manifestContent())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :lib:detektMain")
|
||||
fun libDetektMain() {
|
||||
gradleRunner.runTasksAndCheckResult(":lib:detektMain") { buildResult ->
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":lib:detektOldHarryDebug",
|
||||
":lib:detektOldHarryRelease"
|
||||
)
|
||||
).doesNotContain(
|
||||
":lib:detektYoungHarryDebug"
|
||||
@Nested
|
||||
inner class `configures android tasks for different build variants excluding ignored build types` {
|
||||
|
||||
val projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 0).apply {
|
||||
addSubmodule(
|
||||
name = "lib",
|
||||
numberOfSourceFilesPerSourceDir = 1,
|
||||
numberOfCodeSmells = 1,
|
||||
buildFileContent = """
|
||||
$LIB_PLUGIN_BLOCK
|
||||
$ANDROID_BLOCK_WITH_FLAVOR
|
||||
detekt {
|
||||
ignoredBuildTypes = listOf("release")
|
||||
}
|
||||
""".trimIndent(),
|
||||
srcDirs = listOf("src/main/java", "src/debug/java", "src/test/java", "src/androidTest/java")
|
||||
)
|
||||
}
|
||||
val gradleRunner = createGradleRunnerAndSetupProject(projectLayout).also {
|
||||
it.writeProjectFile("lib/src/main/AndroidManifest.xml", manifestContent())
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :lib:detektMain")
|
||||
fun libDetektMain() {
|
||||
gradleRunner.runTasksAndCheckResult(":lib:detektMain") { buildResult ->
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":lib:detektYoungHarryDebug",
|
||||
":lib:detektOldHarryDebug"
|
||||
)
|
||||
}
|
||||
).doesNotContain(
|
||||
":lib:detektOldHarryRelease"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :lib:detektTest")
|
||||
fun libDetektTest() {
|
||||
gradleRunner.runTasksAndCheckResult(":lib:detektTest") { buildResult ->
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":lib:detektOldHarryDebugUnitTest",
|
||||
":lib:detektOldHarryDebugAndroidTest",
|
||||
":lib:detektOldHarryReleaseUnitTest"
|
||||
)
|
||||
).doesNotContain(
|
||||
@Test
|
||||
@DisplayName("task :lib:detektTest")
|
||||
fun libDetektTest() {
|
||||
gradleRunner.runTasksAndCheckResult(":lib:detektTest") { buildResult ->
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":lib:detektYoungHarryDebugUnitTest",
|
||||
":lib:detektYoungHarryDebugAndroidTest"
|
||||
":lib:detektOldHarryDebugUnitTest",
|
||||
":lib:detektYoungHarryDebugAndroidTest",
|
||||
":lib:detektOldHarryDebugAndroidTest"
|
||||
)
|
||||
}
|
||||
).doesNotContain(
|
||||
":lib:detektOldHarryReleaseUnitTest"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `configures android tasks for different build variants excluding ignored variants` {
|
||||
|
||||
val projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 0).apply {
|
||||
addSubmodule(
|
||||
name = "lib",
|
||||
numberOfSourceFilesPerSourceDir = 1,
|
||||
numberOfCodeSmells = 1,
|
||||
buildFileContent = """
|
||||
$LIB_PLUGIN_BLOCK
|
||||
$ANDROID_BLOCK_WITH_FLAVOR
|
||||
detekt {
|
||||
ignoredVariants = listOf("youngHarryDebug", "oldHarryRelease")
|
||||
}
|
||||
""".trimIndent(),
|
||||
srcDirs = listOf("src/main/java", "src/debug/java", "src/test/java", "src/androidTest/java")
|
||||
)
|
||||
}
|
||||
val gradleRunner = createGradleRunnerAndSetupProject(projectLayout).also {
|
||||
it.writeProjectFile("lib/src/main/AndroidManifest.xml", manifestContent())
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :lib:detektMain")
|
||||
fun libDetektMain() {
|
||||
gradleRunner.runTasksAndCheckResult(":lib:detektMain") { buildResult ->
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":lib:detektOldHarryDebug"
|
||||
)
|
||||
).doesNotContain(
|
||||
":lib:detektYoungHarryDebug",
|
||||
":lib:detektOldHarryRelease"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :lib:detektTest")
|
||||
fun libDetektTest() {
|
||||
gradleRunner.runTasksAndCheckResult(":lib:detektTest") { buildResult ->
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":lib:detektOldHarryDebugUnitTest",
|
||||
":lib:detektOldHarryDebugAndroidTest"
|
||||
)
|
||||
).doesNotContain(
|
||||
":lib:detektYoungHarryDebugUnitTest",
|
||||
":lib:detektYoungHarryDebugAndroidTest",
|
||||
":lib:detektOldHarryReleaseUnitTest"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `configures android tasks for different build variants excluding ignored flavors` {
|
||||
|
||||
val projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 0).apply {
|
||||
addSubmodule(
|
||||
name = "lib",
|
||||
numberOfSourceFilesPerSourceDir = 1,
|
||||
numberOfCodeSmells = 1,
|
||||
buildFileContent = """
|
||||
$LIB_PLUGIN_BLOCK
|
||||
$ANDROID_BLOCK_WITH_FLAVOR
|
||||
detekt {
|
||||
ignoredFlavors = listOf("youngHarry")
|
||||
}
|
||||
""".trimIndent(),
|
||||
srcDirs = listOf("src/main/java", "src/debug/java", "src/test/java", "src/androidTest/java")
|
||||
)
|
||||
}
|
||||
val gradleRunner = createGradleRunnerAndSetupProject(projectLayout).also {
|
||||
it.writeProjectFile("lib/src/main/AndroidManifest.xml", manifestContent())
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :lib:detektMain")
|
||||
fun libDetektMain() {
|
||||
gradleRunner.runTasksAndCheckResult(":lib:detektMain") { buildResult ->
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":lib:detektOldHarryDebug",
|
||||
":lib:detektOldHarryRelease"
|
||||
)
|
||||
).doesNotContain(
|
||||
":lib:detektYoungHarryDebug"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("task :lib:detektTest")
|
||||
fun libDetektTest() {
|
||||
gradleRunner.runTasksAndCheckResult(":lib:detektTest") { buildResult ->
|
||||
assertThat(buildResult.tasks.map { it.path }).containsAll(
|
||||
listOf(
|
||||
":lib:detektOldHarryDebugUnitTest",
|
||||
":lib:detektOldHarryDebugAndroidTest",
|
||||
":lib:detektOldHarryReleaseUnitTest"
|
||||
)
|
||||
).doesNotContain(
|
||||
":lib:detektYoungHarryDebugUnitTest",
|
||||
":lib:detektYoungHarryDebugAndroidTest"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,82 +7,79 @@ import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class DetektJvmSpec {
|
||||
|
||||
@Nested
|
||||
inner class `When applying detekt in a JVM project` {
|
||||
|
||||
@Nested
|
||||
inner class `report location set on extension & task` {
|
||||
val gradleRunner = DslGradleRunner(
|
||||
projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 1),
|
||||
buildFileName = "build.gradle.kts",
|
||||
mainBuildFileContent = """
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("io.gitlab.arturbosch.detekt")
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
detekt {
|
||||
reports {
|
||||
txt.destination = file("output-path.txt")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
|
||||
reports {
|
||||
txt.destination = file("output-path2.txt")
|
||||
}
|
||||
}
|
||||
""".trimIndent(),
|
||||
dryRun = false
|
||||
).also {
|
||||
it.setupProject()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `logs a warning`() {
|
||||
gradleRunner.runTasksAndCheckResult(":detektMain") { buildResult ->
|
||||
assertThat(buildResult.output).contains("TXT report location set on detekt {} extension will be ignored for detektMain task.")
|
||||
inner class `report location set on extension & task` {
|
||||
val gradleRunner = DslGradleRunner(
|
||||
projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 1),
|
||||
buildFileName = "build.gradle.kts",
|
||||
mainBuildFileContent = """
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("io.gitlab.arturbosch.detekt")
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
detekt {
|
||||
reports {
|
||||
txt.destination = file("output-path.txt")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
|
||||
reports {
|
||||
txt.destination = file("output-path2.txt")
|
||||
}
|
||||
}
|
||||
""".trimIndent(),
|
||||
dryRun = false
|
||||
).also {
|
||||
it.setupProject()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `report location set on task only` {
|
||||
val gradleRunner = DslGradleRunner(
|
||||
projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 1),
|
||||
buildFileName = "build.gradle.kts",
|
||||
mainBuildFileContent = """
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("io.gitlab.arturbosch.detekt")
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
|
||||
reports {
|
||||
txt.destination = file("output-path2.txt")
|
||||
}
|
||||
}
|
||||
""".trimIndent(),
|
||||
dryRun = false
|
||||
).also {
|
||||
it.setupProject()
|
||||
@Test
|
||||
fun `logs a warning`() {
|
||||
gradleRunner.runTasksAndCheckResult(":detektMain") { buildResult ->
|
||||
assertThat(buildResult.output).contains("TXT report location set on detekt {} extension will be ignored for detektMain task.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `logs a warning`() {
|
||||
gradleRunner.runTasksAndCheckResult(":detektMain") { buildResult ->
|
||||
assertThat(buildResult.output).doesNotContain("report location set on detekt {} extension will be ignored")
|
||||
@Nested
|
||||
inner class `report location set on task only` {
|
||||
val gradleRunner = DslGradleRunner(
|
||||
projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 1),
|
||||
buildFileName = "build.gradle.kts",
|
||||
mainBuildFileContent = """
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("io.gitlab.arturbosch.detekt")
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
|
||||
reports {
|
||||
txt.destination = file("output-path2.txt")
|
||||
}
|
||||
}
|
||||
""".trimIndent(),
|
||||
dryRun = false
|
||||
).also {
|
||||
it.setupProject()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `logs a warning`() {
|
||||
gradleRunner.runTasksAndCheckResult(":detektMain") { buildResult ->
|
||||
assertThat(buildResult.output).doesNotContain("report location set on detekt {} extension will be ignored")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,231 +6,226 @@ import io.gitlab.arturbosch.detekt.testkit.ProjectLayout
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.gradle.testkit.runner.TaskOutcome
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class DetektTaskMultiModuleSpec {
|
||||
|
||||
@Nested
|
||||
inner class `The Detekt Gradle plugin used in a multi module project` {
|
||||
|
||||
@Test
|
||||
@DisplayName(
|
||||
"it is applied with defaults to all subprojects individually without " +
|
||||
"sources in root project using the subprojects block"
|
||||
)
|
||||
fun applyToSubprojectsWithoutSources() {
|
||||
val projectLayout = ProjectLayout(0).apply {
|
||||
addSubmodule("child1", 2)
|
||||
addSubmodule("child2", 4)
|
||||
}
|
||||
|
||||
val builder = DslTestBuilder.kotlin()
|
||||
|
||||
val mainBuildFileContent: String = """
|
||||
|${builder.gradlePlugins}
|
||||
|
|
||||
|allprojects {
|
||||
| ${builder.gradleRepositories}
|
||||
|}
|
||||
|subprojects {
|
||||
| ${builder.gradleSubprojectsApplyPlugins}
|
||||
|}
|
||||
|
|
||||
""".trimMargin()
|
||||
|
||||
val gradleRunner = DslGradleRunner(projectLayout, builder.gradleBuildName, mainBuildFileContent)
|
||||
|
||||
gradleRunner.setupProject()
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.NO_SOURCE)
|
||||
projectLayout.submodules.forEach { submodule ->
|
||||
assertThat(result.task(":${submodule.name}:detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
|
||||
assertThat(projectFile("build/reports/detekt/detekt.xml")).doesNotExist()
|
||||
assertThat(projectFile("build/reports/detekt/detekt.html")).doesNotExist()
|
||||
assertThat(projectFile("build/reports/detekt/detekt.txt")).doesNotExist()
|
||||
projectLayout.submodules.forEach {
|
||||
assertThat(projectFile("${it.name}/build/reports/detekt/detekt.xml")).exists()
|
||||
assertThat(projectFile("${it.name}/build/reports/detekt/detekt.html")).exists()
|
||||
assertThat(projectFile("${it.name}/build/reports/detekt/detekt.txt")).exists()
|
||||
}
|
||||
}
|
||||
@Test
|
||||
@DisplayName(
|
||||
"it is applied with defaults to all subprojects individually without " +
|
||||
"sources in root project using the subprojects block"
|
||||
)
|
||||
fun applyToSubprojectsWithoutSources() {
|
||||
val projectLayout = ProjectLayout(0).apply {
|
||||
addSubmodule("child1", 2)
|
||||
addSubmodule("child2", 4)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName(
|
||||
"it is applied with defaults to main project and subprojects " +
|
||||
"individually using the allprojects block"
|
||||
)
|
||||
fun applyWithAllprojectsBlock() {
|
||||
val projectLayout = ProjectLayout(1).apply {
|
||||
addSubmodule("child1", 2)
|
||||
addSubmodule("child2", 4)
|
||||
val builder = DslTestBuilder.kotlin()
|
||||
|
||||
val mainBuildFileContent: String = """
|
||||
|${builder.gradlePlugins}
|
||||
|
|
||||
|allprojects {
|
||||
| ${builder.gradleRepositories}
|
||||
|}
|
||||
|subprojects {
|
||||
| ${builder.gradleSubprojectsApplyPlugins}
|
||||
|}
|
||||
|
|
||||
""".trimMargin()
|
||||
|
||||
val gradleRunner = DslGradleRunner(projectLayout, builder.gradleBuildName, mainBuildFileContent)
|
||||
|
||||
gradleRunner.setupProject()
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.NO_SOURCE)
|
||||
projectLayout.submodules.forEach { submodule ->
|
||||
assertThat(result.task(":${submodule.name}:detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
|
||||
val builder = DslTestBuilder.kotlin()
|
||||
|
||||
val mainBuildFileContent: String = """
|
||||
|${builder.gradlePlugins}
|
||||
|
|
||||
|allprojects {
|
||||
| ${builder.gradleRepositories}
|
||||
| ${builder.gradleSubprojectsApplyPlugins}
|
||||
|}
|
||||
|
|
||||
""".trimMargin()
|
||||
|
||||
val gradleRunner = DslGradleRunner(projectLayout, builder.gradleBuildName, mainBuildFileContent)
|
||||
|
||||
gradleRunner.setupProject()
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
projectLayout.submodules.forEach { submodule ->
|
||||
assertThat(result.task(":${submodule.name}:detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
|
||||
assertThat(projectFile("build/reports/detekt/detekt.xml")).exists()
|
||||
assertThat(projectFile("build/reports/detekt/detekt.html")).exists()
|
||||
assertThat(projectFile("build/reports/detekt/detekt.txt")).exists()
|
||||
projectLayout.submodules.forEach {
|
||||
assertThat(projectFile("${it.name}/build/reports/detekt/detekt.xml")).exists()
|
||||
assertThat(projectFile("${it.name}/build/reports/detekt/detekt.html")).exists()
|
||||
assertThat(projectFile("${it.name}/build/reports/detekt/detekt.txt")).exists()
|
||||
}
|
||||
assertThat(projectFile("build/reports/detekt/detekt.xml")).doesNotExist()
|
||||
assertThat(projectFile("build/reports/detekt/detekt.html")).doesNotExist()
|
||||
assertThat(projectFile("build/reports/detekt/detekt.txt")).doesNotExist()
|
||||
projectLayout.submodules.forEach {
|
||||
assertThat(projectFile("${it.name}/build/reports/detekt/detekt.xml")).exists()
|
||||
assertThat(projectFile("${it.name}/build/reports/detekt/detekt.html")).exists()
|
||||
assertThat(projectFile("${it.name}/build/reports/detekt/detekt.txt")).exists()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `it uses custom configs when configured in allprojects block`() {
|
||||
val projectLayout = ProjectLayout(1).apply {
|
||||
addSubmodule("child1", 2)
|
||||
addSubmodule("child2", 4)
|
||||
}
|
||||
|
||||
val builder = DslTestBuilder.kotlin()
|
||||
|
||||
val mainBuildFileContent: String = """
|
||||
|${builder.gradlePlugins}
|
||||
|
|
||||
|allprojects {
|
||||
| ${builder.gradleRepositories}
|
||||
| ${builder.gradleSubprojectsApplyPlugins}
|
||||
|
|
||||
| detekt {
|
||||
| reportsDir = file("build/detekt-reports")
|
||||
| }
|
||||
|}
|
||||
|
|
||||
""".trimMargin()
|
||||
|
||||
val gradleRunner = DslGradleRunner(projectLayout, builder.gradleBuildName, mainBuildFileContent)
|
||||
gradleRunner.setupProject()
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
projectLayout.submodules.forEach { submodule ->
|
||||
assertThat(result.task(":${submodule.name}:detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
|
||||
assertThat(projectFile("build/detekt-reports/detekt.xml")).exists()
|
||||
assertThat(projectFile("build/detekt-reports/detekt.html")).exists()
|
||||
assertThat(projectFile("build/detekt-reports/detekt.txt")).exists()
|
||||
projectLayout.submodules.forEach {
|
||||
assertThat(projectFile("${it.name}/build/detekt-reports/detekt.xml")).exists()
|
||||
assertThat(projectFile("${it.name}/build/detekt-reports/detekt.html")).exists()
|
||||
assertThat(projectFile("${it.name}/build/detekt-reports/detekt.txt")).exists()
|
||||
}
|
||||
}
|
||||
@Test
|
||||
@DisplayName(
|
||||
"it is applied with defaults to main project and subprojects " +
|
||||
"individually using the allprojects block"
|
||||
)
|
||||
fun applyWithAllprojectsBlock() {
|
||||
val projectLayout = ProjectLayout(1).apply {
|
||||
addSubmodule("child1", 2)
|
||||
addSubmodule("child2", 4)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("it allows changing defaults in allprojects block that can be overwritten in subprojects")
|
||||
fun allowsChangingDefaultsInAllProjectsThatAreOverwrittenInSubprojects() {
|
||||
val child2DetektConfig = """
|
||||
|detekt {
|
||||
| reportsDir = file("build/custom")
|
||||
|}
|
||||
|
|
||||
""".trimMargin()
|
||||
val builder = DslTestBuilder.kotlin()
|
||||
|
||||
val projectLayout = ProjectLayout(1).apply {
|
||||
addSubmodule("child1", 2)
|
||||
addSubmodule("child2", 4, buildFileContent = child2DetektConfig)
|
||||
val mainBuildFileContent: String = """
|
||||
|${builder.gradlePlugins}
|
||||
|
|
||||
|allprojects {
|
||||
| ${builder.gradleRepositories}
|
||||
| ${builder.gradleSubprojectsApplyPlugins}
|
||||
|}
|
||||
|
|
||||
""".trimMargin()
|
||||
|
||||
val gradleRunner = DslGradleRunner(projectLayout, builder.gradleBuildName, mainBuildFileContent)
|
||||
|
||||
gradleRunner.setupProject()
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
projectLayout.submodules.forEach { submodule ->
|
||||
assertThat(result.task(":${submodule.name}:detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
|
||||
val builder = DslTestBuilder.kotlin()
|
||||
|
||||
val mainBuildFileContent: String = """
|
||||
|${builder.gradlePlugins}
|
||||
|
|
||||
|allprojects {
|
||||
| ${builder.gradleRepositories}
|
||||
| ${builder.gradleSubprojectsApplyPlugins}
|
||||
|
|
||||
| detekt {
|
||||
| reportsDir = file("build/detekt-reports")
|
||||
| }
|
||||
|}
|
||||
|
|
||||
""".trimMargin()
|
||||
|
||||
val gradleRunner = DslGradleRunner(projectLayout, builder.gradleBuildName, mainBuildFileContent)
|
||||
|
||||
gradleRunner.setupProject()
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
projectLayout.submodules.forEach { submodule ->
|
||||
assertThat(result.task(":${submodule.name}:detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
|
||||
assertThat(projectFile("build/detekt-reports/detekt.xml")).exists()
|
||||
assertThat(projectFile("build/detekt-reports/detekt.html")).exists()
|
||||
assertThat(projectFile("build/detekt-reports/detekt.txt")).exists()
|
||||
assertThat(projectFile("child1/build/detekt-reports/detekt.xml")).exists()
|
||||
assertThat(projectFile("child1/build/detekt-reports/detekt.html")).exists()
|
||||
assertThat(projectFile("child1/build/detekt-reports/detekt.txt")).exists()
|
||||
assertThat(projectFile("child2/build/custom/detekt.xml")).exists()
|
||||
assertThat(projectFile("child2/build/custom/detekt.html")).exists()
|
||||
assertThat(projectFile("child2/build/custom/detekt.txt")).exists()
|
||||
assertThat(projectFile("build/reports/detekt/detekt.xml")).exists()
|
||||
assertThat(projectFile("build/reports/detekt/detekt.html")).exists()
|
||||
assertThat(projectFile("build/reports/detekt/detekt.txt")).exists()
|
||||
projectLayout.submodules.forEach {
|
||||
assertThat(projectFile("${it.name}/build/reports/detekt/detekt.xml")).exists()
|
||||
assertThat(projectFile("${it.name}/build/reports/detekt/detekt.html")).exists()
|
||||
assertThat(projectFile("${it.name}/build/reports/detekt/detekt.txt")).exists()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `it can be applied to all files in entire project resulting in 1 report`() {
|
||||
val projectLayout = ProjectLayout(1).apply {
|
||||
addSubmodule("child1", 2)
|
||||
addSubmodule("child2", 4)
|
||||
@Test
|
||||
fun `it uses custom configs when configured in allprojects block`() {
|
||||
val projectLayout = ProjectLayout(1).apply {
|
||||
addSubmodule("child1", 2)
|
||||
addSubmodule("child2", 4)
|
||||
}
|
||||
|
||||
val builder = DslTestBuilder.kotlin()
|
||||
|
||||
val mainBuildFileContent: String = """
|
||||
|${builder.gradlePlugins}
|
||||
|
|
||||
|allprojects {
|
||||
| ${builder.gradleRepositories}
|
||||
| ${builder.gradleSubprojectsApplyPlugins}
|
||||
|
|
||||
| detekt {
|
||||
| reportsDir = file("build/detekt-reports")
|
||||
| }
|
||||
|}
|
||||
|
|
||||
""".trimMargin()
|
||||
|
||||
val gradleRunner = DslGradleRunner(projectLayout, builder.gradleBuildName, mainBuildFileContent)
|
||||
gradleRunner.setupProject()
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
projectLayout.submodules.forEach { submodule ->
|
||||
assertThat(result.task(":${submodule.name}:detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
|
||||
val detektConfig: String = """
|
||||
|detekt {
|
||||
| source = files(
|
||||
| "${"$"}projectDir/src",
|
||||
| "${"$"}projectDir/child1/src",
|
||||
| "${"$"}projectDir/child2/src"
|
||||
| )
|
||||
|}
|
||||
""".trimMargin()
|
||||
val gradleRunner = DslTestBuilder.kotlin()
|
||||
.withProjectLayout(projectLayout)
|
||||
.withDetektConfig(detektConfig)
|
||||
.build()
|
||||
assertThat(projectFile("build/detekt-reports/detekt.xml")).exists()
|
||||
assertThat(projectFile("build/detekt-reports/detekt.html")).exists()
|
||||
assertThat(projectFile("build/detekt-reports/detekt.txt")).exists()
|
||||
projectLayout.submodules.forEach {
|
||||
assertThat(projectFile("${it.name}/build/detekt-reports/detekt.xml")).exists()
|
||||
assertThat(projectFile("${it.name}/build/detekt-reports/detekt.html")).exists()
|
||||
assertThat(projectFile("${it.name}/build/detekt-reports/detekt.txt")).exists()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
projectLayout.submodules.forEach { submodule ->
|
||||
assertThat(result.task(":${submodule.name}:detekt")).isNull()
|
||||
}
|
||||
@Test
|
||||
@DisplayName("it allows changing defaults in allprojects block that can be overwritten in subprojects")
|
||||
fun allowsChangingDefaultsInAllProjectsThatAreOverwrittenInSubprojects() {
|
||||
val child2DetektConfig = """
|
||||
|detekt {
|
||||
| reportsDir = file("build/custom")
|
||||
|}
|
||||
|
|
||||
""".trimMargin()
|
||||
|
||||
assertThat(projectFile("build/reports/detekt/detekt.xml")).exists()
|
||||
assertThat(projectFile("build/reports/detekt/detekt.html")).exists()
|
||||
assertThat(projectFile("build/reports/detekt/detekt.txt")).exists()
|
||||
projectLayout.submodules.forEach { submodule ->
|
||||
assertThat(projectFile("${submodule.name}/build/reports/detekt/detekt.xml")).doesNotExist()
|
||||
assertThat(projectFile("${submodule.name}/build/reports/detekt/detekt.html")).doesNotExist()
|
||||
assertThat(projectFile("${submodule.name}/build/reports/detekt/detekt.txt")).doesNotExist()
|
||||
}
|
||||
val projectLayout = ProjectLayout(1).apply {
|
||||
addSubmodule("child1", 2)
|
||||
addSubmodule("child2", 4, buildFileContent = child2DetektConfig)
|
||||
}
|
||||
|
||||
val builder = DslTestBuilder.kotlin()
|
||||
|
||||
val mainBuildFileContent: String = """
|
||||
|${builder.gradlePlugins}
|
||||
|
|
||||
|allprojects {
|
||||
| ${builder.gradleRepositories}
|
||||
| ${builder.gradleSubprojectsApplyPlugins}
|
||||
|
|
||||
| detekt {
|
||||
| reportsDir = file("build/detekt-reports")
|
||||
| }
|
||||
|}
|
||||
|
|
||||
""".trimMargin()
|
||||
|
||||
val gradleRunner = DslGradleRunner(projectLayout, builder.gradleBuildName, mainBuildFileContent)
|
||||
|
||||
gradleRunner.setupProject()
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
projectLayout.submodules.forEach { submodule ->
|
||||
assertThat(result.task(":${submodule.name}:detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
|
||||
assertThat(projectFile("build/detekt-reports/detekt.xml")).exists()
|
||||
assertThat(projectFile("build/detekt-reports/detekt.html")).exists()
|
||||
assertThat(projectFile("build/detekt-reports/detekt.txt")).exists()
|
||||
assertThat(projectFile("child1/build/detekt-reports/detekt.xml")).exists()
|
||||
assertThat(projectFile("child1/build/detekt-reports/detekt.html")).exists()
|
||||
assertThat(projectFile("child1/build/detekt-reports/detekt.txt")).exists()
|
||||
assertThat(projectFile("child2/build/custom/detekt.xml")).exists()
|
||||
assertThat(projectFile("child2/build/custom/detekt.html")).exists()
|
||||
assertThat(projectFile("child2/build/custom/detekt.txt")).exists()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `it can be applied to all files in entire project resulting in 1 report`() {
|
||||
val projectLayout = ProjectLayout(1).apply {
|
||||
addSubmodule("child1", 2)
|
||||
addSubmodule("child2", 4)
|
||||
}
|
||||
|
||||
val detektConfig: String = """
|
||||
|detekt {
|
||||
| source = files(
|
||||
| "${"$"}projectDir/src",
|
||||
| "${"$"}projectDir/child1/src",
|
||||
| "${"$"}projectDir/child2/src"
|
||||
| )
|
||||
|}
|
||||
""".trimMargin()
|
||||
val gradleRunner = DslTestBuilder.kotlin()
|
||||
.withProjectLayout(projectLayout)
|
||||
.withDetektConfig(detektConfig)
|
||||
.build()
|
||||
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
projectLayout.submodules.forEach { submodule ->
|
||||
assertThat(result.task(":${submodule.name}:detekt")).isNull()
|
||||
}
|
||||
|
||||
assertThat(projectFile("build/reports/detekt/detekt.xml")).exists()
|
||||
assertThat(projectFile("build/reports/detekt/detekt.html")).exists()
|
||||
assertThat(projectFile("build/reports/detekt/detekt.txt")).exists()
|
||||
projectLayout.submodules.forEach { submodule ->
|
||||
assertThat(projectFile("${submodule.name}/build/reports/detekt/detekt.xml")).doesNotExist()
|
||||
assertThat(projectFile("${submodule.name}/build/reports/detekt/detekt.html")).doesNotExist()
|
||||
assertThat(projectFile("${submodule.name}/build/reports/detekt/detekt.txt")).doesNotExist()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import io.gitlab.arturbosch.detekt.testkit.DslTestBuilder.Companion.kotlin
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.gradle.testkit.runner.TaskOutcome
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
/**
|
||||
@@ -24,106 +23,103 @@ class PluginTaskBehaviorSpec {
|
||||
|}
|
||||
"""
|
||||
|
||||
@Nested
|
||||
inner class `The Detekt Gradle Plugin 'detekt' Task` {
|
||||
lateinit var gradleRunner: DslGradleRunner
|
||||
lateinit var gradleRunner: DslGradleRunner
|
||||
|
||||
@BeforeEach
|
||||
fun setupGradleRunner() {
|
||||
gradleRunner = kotlin()
|
||||
.withDetektConfig(detektConfig)
|
||||
.withBaseline(baselineFileName)
|
||||
.withConfigFile(configFileName)
|
||||
.build()
|
||||
@BeforeEach
|
||||
fun setupGradleRunner() {
|
||||
gradleRunner = kotlin()
|
||||
.withDetektConfig(detektConfig)
|
||||
.withBaseline(baselineFileName)
|
||||
.withConfigFile(configFileName)
|
||||
.build()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should be UP-TO-DATE the 2nd run without changes`() {
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should pick up build artifacts from the build cache on a 2nd run after deleting the build dir`() {
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should be UP-TO-DATE the 2nd run without changes`() {
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
|
||||
}
|
||||
gradleRunner.projectFile("build").deleteRecursively()
|
||||
|
||||
// Running detekt again should pick up artifacts from Build Cache
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.FROM_CACHE)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should pick up build artifacts from the build cache on a 2nd run after running 'clean'`() {
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
gradleRunner.runTasksAndCheckResult("clean", "detekt") { result ->
|
||||
assertThat(result.task(":clean")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.FROM_CACHE)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should run again after changing config`() {
|
||||
val configFileWithCommentsDisabled = """
|
||||
|comments:
|
||||
| active: false
|
||||
""".trimMargin()
|
||||
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should pick up build artifacts from the build cache on a 2nd run after deleting the build dir`() {
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
// update config file
|
||||
gradleRunner.writeProjectFile(configFileName, configFileWithCommentsDisabled)
|
||||
|
||||
gradleRunner.projectFile("build").deleteRecursively()
|
||||
gradleRunner.runTasksAndCheckResult("detekt") { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
}
|
||||
|
||||
// Running detekt again should pick up artifacts from Build Cache
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.FROM_CACHE)
|
||||
}
|
||||
@Test
|
||||
fun `should run again after changing baseline`() {
|
||||
val changedBaselineContent = """
|
||||
|<some>
|
||||
| <more/>
|
||||
| <xml/>
|
||||
|</some>
|
||||
""".trimMargin()
|
||||
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should pick up build artifacts from the build cache on a 2nd run after running 'clean'`() {
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
gradleRunner.runTasksAndCheckResult("clean", "detekt") { result ->
|
||||
assertThat(result.task(":clean")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.FROM_CACHE)
|
||||
}
|
||||
// update baseline file
|
||||
gradleRunner.writeProjectFile(baselineFileName, changedBaselineContent)
|
||||
|
||||
gradleRunner.runTasksAndCheckResult("detekt") { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should run again after changing inputs`() {
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should run again after changing config`() {
|
||||
val configFileWithCommentsDisabled = """
|
||||
|comments:
|
||||
| active: false
|
||||
""".trimMargin()
|
||||
// add a new File
|
||||
gradleRunner.writeKtFile(gradleRunner.projectLayout.srcDirs.first(), "OtherKotlinClass")
|
||||
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
|
||||
// update config file
|
||||
gradleRunner.writeProjectFile(configFileName, configFileWithCommentsDisabled)
|
||||
|
||||
gradleRunner.runTasksAndCheckResult("detekt") { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should run again after changing baseline`() {
|
||||
val changedBaselineContent = """
|
||||
|<some>
|
||||
| <more/>
|
||||
| <xml/>
|
||||
|</some>
|
||||
""".trimMargin()
|
||||
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
|
||||
// update baseline file
|
||||
gradleRunner.writeProjectFile(baselineFileName, changedBaselineContent)
|
||||
|
||||
gradleRunner.runTasksAndCheckResult("detekt") { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should run again after changing inputs`() {
|
||||
gradleRunner.runDetektTaskAndCheckResult { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
|
||||
// add a new File
|
||||
gradleRunner.writeKtFile(gradleRunner.projectLayout.srcDirs.first(), "OtherKotlinClass")
|
||||
|
||||
gradleRunner.runTasksAndCheckResult("detekt") { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
gradleRunner.runTasksAndCheckResult("detekt") { result ->
|
||||
assertThat(result.task(":detekt")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import io.gitlab.arturbosch.detekt.testkit.DslTestBuilder
|
||||
import io.gitlab.arturbosch.detekt.testkit.ProjectLayout
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.gradle.testkit.runner.TaskOutcome
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.condition.EnabledForJreRange
|
||||
import org.junit.jupiter.api.condition.EnabledIf
|
||||
@@ -14,187 +13,183 @@ import org.junit.jupiter.api.condition.JRE.JAVA_11
|
||||
|
||||
class ReportMergeSpec {
|
||||
|
||||
@Nested
|
||||
inner class `Merging reports in a multi module projects` {
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Test
|
||||
fun `for jvm detekt`() {
|
||||
val builder = DslTestBuilder.kotlin()
|
||||
val projectLayout = ProjectLayout(0).apply {
|
||||
addSubmodule(
|
||||
"child1",
|
||||
numberOfSourceFilesPerSourceDir = 2,
|
||||
buildFileContent = """
|
||||
${builder.gradleSubprojectsApplyPlugins}
|
||||
|plugins.apply("java-library")
|
||||
""".trimMargin()
|
||||
)
|
||||
addSubmodule(
|
||||
"child2",
|
||||
numberOfSourceFilesPerSourceDir = 2,
|
||||
buildFileContent = """
|
||||
${builder.gradleSubprojectsApplyPlugins}
|
||||
|plugins.apply("java-library")
|
||||
""".trimMargin()
|
||||
)
|
||||
}
|
||||
val mainBuildFileContent: String = """
|
||||
|plugins {
|
||||
| id("io.gitlab.arturbosch.detekt")
|
||||
|}
|
||||
|
|
||||
|allprojects {
|
||||
| ${builder.gradleRepositories}
|
||||
|}
|
||||
|
|
||||
|val reportMerge by tasks.registering(io.gitlab.arturbosch.detekt.report.ReportMergeTask::class) {
|
||||
| output.set(project.layout.buildDirectory.file("reports/detekt/merge.xml"))
|
||||
| outputs.cacheIf { false }
|
||||
| outputs.upToDateWhen { false }
|
||||
|}
|
||||
|
|
||||
|subprojects {
|
||||
| apply(plugin = "org.jetbrains.kotlin.jvm")
|
||||
| apply(plugin = "io.gitlab.arturbosch.detekt")
|
||||
|
|
||||
| detekt {
|
||||
| reports.xml.enabled = true
|
||||
| }
|
||||
|
|
||||
| plugins.withType<io.gitlab.arturbosch.detekt.DetektPlugin> {
|
||||
| tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
|
||||
| finalizedBy(reportMerge)
|
||||
| reportMerge.configure { input.from(xmlReportFile) }
|
||||
| }
|
||||
| }
|
||||
|}
|
||||
""".trimMargin()
|
||||
|
||||
val gradleRunner = DslGradleRunner(
|
||||
projectLayout = projectLayout,
|
||||
buildFileName = builder.gradleBuildName,
|
||||
mainBuildFileContent = mainBuildFileContent
|
||||
@Suppress("LongMethod")
|
||||
@Test
|
||||
fun `for jvm detekt`() {
|
||||
val builder = DslTestBuilder.kotlin()
|
||||
val projectLayout = ProjectLayout(0).apply {
|
||||
addSubmodule(
|
||||
"child1",
|
||||
numberOfSourceFilesPerSourceDir = 2,
|
||||
buildFileContent = """
|
||||
${builder.gradleSubprojectsApplyPlugins}
|
||||
|plugins.apply("java-library")
|
||||
""".trimMargin()
|
||||
)
|
||||
addSubmodule(
|
||||
"child2",
|
||||
numberOfSourceFilesPerSourceDir = 2,
|
||||
buildFileContent = """
|
||||
${builder.gradleSubprojectsApplyPlugins}
|
||||
|plugins.apply("java-library")
|
||||
""".trimMargin()
|
||||
)
|
||||
|
||||
gradleRunner.setupProject()
|
||||
gradleRunner.runTasksAndCheckResult("detektMain", "reportMerge", "--continue") { result ->
|
||||
projectLayout.submodules.forEach { submodule ->
|
||||
assertThat(result.task(":${submodule.name}:detektMain")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
|
||||
projectLayout.submodules.forEach {
|
||||
assertThat(projectFile("${it.name}/build/reports/detekt/main.xml")).exists()
|
||||
}
|
||||
|
||||
assertThat(projectFile("build/reports/detekt/merge.xml")).exists()
|
||||
}
|
||||
}
|
||||
val mainBuildFileContent: String = """
|
||||
|plugins {
|
||||
| id("io.gitlab.arturbosch.detekt")
|
||||
|}
|
||||
|
|
||||
|allprojects {
|
||||
| ${builder.gradleRepositories}
|
||||
|}
|
||||
|
|
||||
|val reportMerge by tasks.registering(io.gitlab.arturbosch.detekt.report.ReportMergeTask::class) {
|
||||
| output.set(project.layout.buildDirectory.file("reports/detekt/merge.xml"))
|
||||
| outputs.cacheIf { false }
|
||||
| outputs.upToDateWhen { false }
|
||||
|}
|
||||
|
|
||||
|subprojects {
|
||||
| apply(plugin = "org.jetbrains.kotlin.jvm")
|
||||
| apply(plugin = "io.gitlab.arturbosch.detekt")
|
||||
|
|
||||
| detekt {
|
||||
| reports.xml.enabled = true
|
||||
| }
|
||||
|
|
||||
| plugins.withType<io.gitlab.arturbosch.detekt.DetektPlugin> {
|
||||
| tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
|
||||
| finalizedBy(reportMerge)
|
||||
| reportMerge.configure { input.from(xmlReportFile) }
|
||||
| }
|
||||
| }
|
||||
|}
|
||||
""".trimMargin()
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Test
|
||||
@EnabledForJreRange(min = JAVA_11, disabledReason = "Android Gradle Plugin 7.0+ requires JDK 11 or newer")
|
||||
@EnabledIf("io.gitlab.arturbosch.detekt.DetektAndroidSpecKt#isAndroidSdkInstalled")
|
||||
fun `for android detekt`() {
|
||||
val builder = DslTestBuilder.kotlin()
|
||||
val projectLayout = ProjectLayout(0).apply {
|
||||
addSubmodule(
|
||||
name = "app",
|
||||
numberOfSourceFilesPerSourceDir = 1,
|
||||
buildFileContent = """
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
kotlin("android")
|
||||
id("io.gitlab.arturbosch.detekt")
|
||||
}
|
||||
android {
|
||||
compileSdkVersion(30)
|
||||
}
|
||||
dependencies {
|
||||
implementation(project(":lib"))
|
||||
}
|
||||
""".trimIndent(),
|
||||
srcDirs = listOf("src/main/java", "src/debug/java", "src/test/java", "src/androidTest/java"),
|
||||
)
|
||||
addSubmodule(
|
||||
name = "lib",
|
||||
numberOfSourceFilesPerSourceDir = 1,
|
||||
buildFileContent = """
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
kotlin("android")
|
||||
}
|
||||
android {
|
||||
compileSdkVersion(30)
|
||||
}
|
||||
""".trimIndent(),
|
||||
srcDirs = listOf("src/main/java", "src/debug/java", "src/test/java", "src/androidTest/java")
|
||||
)
|
||||
val gradleRunner = DslGradleRunner(
|
||||
projectLayout = projectLayout,
|
||||
buildFileName = builder.gradleBuildName,
|
||||
mainBuildFileContent = mainBuildFileContent
|
||||
)
|
||||
|
||||
gradleRunner.setupProject()
|
||||
gradleRunner.runTasksAndCheckResult("detektMain", "reportMerge", "--continue") { result ->
|
||||
projectLayout.submodules.forEach { submodule ->
|
||||
assertThat(result.task(":${submodule.name}:detektMain")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
val mainBuildFileContent: String = """
|
||||
|plugins {
|
||||
| id("io.gitlab.arturbosch.detekt")
|
||||
|}
|
||||
|
|
||||
|allprojects {
|
||||
| repositories {
|
||||
| mavenCentral()
|
||||
| google()
|
||||
| mavenLocal()
|
||||
| }
|
||||
|}
|
||||
|
|
||||
|val reportMerge by tasks.registering(io.gitlab.arturbosch.detekt.report.ReportMergeTask::class) {
|
||||
| output.set(project.layout.buildDirectory.file("reports/detekt/merge.xml"))
|
||||
| outputs.cacheIf { false }
|
||||
| outputs.upToDateWhen { false }
|
||||
|}
|
||||
|
|
||||
|subprojects {
|
||||
| apply(plugin = "io.gitlab.arturbosch.detekt")
|
||||
|
|
||||
| detekt {
|
||||
| reports.xml.enabled = true
|
||||
| }
|
||||
|
|
||||
| plugins.withType<io.gitlab.arturbosch.detekt.DetektPlugin> {
|
||||
| tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
|
||||
| finalizedBy(reportMerge)
|
||||
| reportMerge.configure { input.from(xmlReportFile) }
|
||||
| }
|
||||
| }
|
||||
|}
|
||||
""".trimMargin()
|
||||
|
||||
val jvmArgs = "-Xmx2g -XX:MaxMetaspaceSize=1g"
|
||||
|
||||
val gradleRunner = DslGradleRunner(
|
||||
projectLayout = projectLayout,
|
||||
buildFileName = builder.gradleBuildName,
|
||||
mainBuildFileContent = mainBuildFileContent,
|
||||
jvmArgs = jvmArgs
|
||||
)
|
||||
|
||||
gradleRunner.setupProject()
|
||||
gradleRunner.writeProjectFile(
|
||||
"app/src/main/AndroidManifest.xml",
|
||||
manifestContent("io.github.detekt.app")
|
||||
)
|
||||
gradleRunner.writeProjectFile(
|
||||
"lib/src/main/AndroidManifest.xml",
|
||||
manifestContent("io.github.detekt.lib")
|
||||
)
|
||||
gradleRunner.runTasksAndCheckResult("detektMain", "reportMerge", "--continue") { result ->
|
||||
projectLayout.submodules.forEach { submodule ->
|
||||
assertThat(result.task(":${submodule.name}:detektMain")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
|
||||
projectLayout.submodules.forEach {
|
||||
assertThat(projectFile("${it.name}/build/reports/detekt/debug.xml")).exists()
|
||||
}
|
||||
// #4192 this should exist by default
|
||||
assertThat(projectFile("build/reports/detekt/merge.xml")).doesNotExist()
|
||||
projectLayout.submodules.forEach {
|
||||
assertThat(projectFile("${it.name}/build/reports/detekt/main.xml")).exists()
|
||||
}
|
||||
|
||||
assertThat(projectFile("build/reports/detekt/merge.xml")).exists()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Test
|
||||
@EnabledForJreRange(min = JAVA_11, disabledReason = "Android Gradle Plugin 7.0+ requires JDK 11 or newer")
|
||||
@EnabledIf("io.gitlab.arturbosch.detekt.DetektAndroidSpecKt#isAndroidSdkInstalled")
|
||||
fun `for android detekt`() {
|
||||
val builder = DslTestBuilder.kotlin()
|
||||
val projectLayout = ProjectLayout(0).apply {
|
||||
addSubmodule(
|
||||
name = "app",
|
||||
numberOfSourceFilesPerSourceDir = 1,
|
||||
buildFileContent = """
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
kotlin("android")
|
||||
id("io.gitlab.arturbosch.detekt")
|
||||
}
|
||||
android {
|
||||
compileSdkVersion(30)
|
||||
}
|
||||
dependencies {
|
||||
implementation(project(":lib"))
|
||||
}
|
||||
""".trimIndent(),
|
||||
srcDirs = listOf("src/main/java", "src/debug/java", "src/test/java", "src/androidTest/java"),
|
||||
)
|
||||
addSubmodule(
|
||||
name = "lib",
|
||||
numberOfSourceFilesPerSourceDir = 1,
|
||||
buildFileContent = """
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
kotlin("android")
|
||||
}
|
||||
android {
|
||||
compileSdkVersion(30)
|
||||
}
|
||||
""".trimIndent(),
|
||||
srcDirs = listOf("src/main/java", "src/debug/java", "src/test/java", "src/androidTest/java")
|
||||
)
|
||||
}
|
||||
val mainBuildFileContent: String = """
|
||||
|plugins {
|
||||
| id("io.gitlab.arturbosch.detekt")
|
||||
|}
|
||||
|
|
||||
|allprojects {
|
||||
| repositories {
|
||||
| mavenCentral()
|
||||
| google()
|
||||
| mavenLocal()
|
||||
| }
|
||||
|}
|
||||
|
|
||||
|val reportMerge by tasks.registering(io.gitlab.arturbosch.detekt.report.ReportMergeTask::class) {
|
||||
| output.set(project.layout.buildDirectory.file("reports/detekt/merge.xml"))
|
||||
| outputs.cacheIf { false }
|
||||
| outputs.upToDateWhen { false }
|
||||
|}
|
||||
|
|
||||
|subprojects {
|
||||
| apply(plugin = "io.gitlab.arturbosch.detekt")
|
||||
|
|
||||
| detekt {
|
||||
| reports.xml.enabled = true
|
||||
| }
|
||||
|
|
||||
| plugins.withType<io.gitlab.arturbosch.detekt.DetektPlugin> {
|
||||
| tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
|
||||
| finalizedBy(reportMerge)
|
||||
| reportMerge.configure { input.from(xmlReportFile) }
|
||||
| }
|
||||
| }
|
||||
|}
|
||||
""".trimMargin()
|
||||
|
||||
val jvmArgs = "-Xmx2g -XX:MaxMetaspaceSize=1g"
|
||||
|
||||
val gradleRunner = DslGradleRunner(
|
||||
projectLayout = projectLayout,
|
||||
buildFileName = builder.gradleBuildName,
|
||||
mainBuildFileContent = mainBuildFileContent,
|
||||
jvmArgs = jvmArgs
|
||||
)
|
||||
|
||||
gradleRunner.setupProject()
|
||||
gradleRunner.writeProjectFile(
|
||||
"app/src/main/AndroidManifest.xml",
|
||||
manifestContent("io.github.detekt.app")
|
||||
)
|
||||
gradleRunner.writeProjectFile(
|
||||
"lib/src/main/AndroidManifest.xml",
|
||||
manifestContent("io.github.detekt.lib")
|
||||
)
|
||||
gradleRunner.runTasksAndCheckResult("detektMain", "reportMerge", "--continue") { result ->
|
||||
projectLayout.submodules.forEach { submodule ->
|
||||
assertThat(result.task(":${submodule.name}:detektMain")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
|
||||
}
|
||||
|
||||
projectLayout.submodules.forEach {
|
||||
assertThat(projectFile("${it.name}/build/reports/detekt/debug.xml")).exists()
|
||||
}
|
||||
// #4192 this should exist by default
|
||||
assertThat(projectFile("build/reports/detekt/merge.xml")).doesNotExist()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,62 +6,54 @@ import org.assertj.core.api.Assertions.assertThat
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.repositories
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class DetektJvmSpec {
|
||||
@Nested
|
||||
inner class `When applying detekt in a JVM project` {
|
||||
|
||||
@Nested
|
||||
inner class `disabled TXT report` {
|
||||
|
||||
val gradleRunner = DslGradleRunner(
|
||||
projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 1),
|
||||
buildFileName = "build.gradle.kts",
|
||||
baselineFiles = listOf("detekt-baseline.xml", "detekt-baseline-main.xml", "detekt-baseline-test.xml"),
|
||||
projectScript = {
|
||||
apply<KotlinPluginWrapper>()
|
||||
apply<DetektPlugin>()
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
tasks.withType(Detekt::class.java).configureEach {
|
||||
it.reports { reports ->
|
||||
reports.txt.required.set(false)
|
||||
}
|
||||
}
|
||||
},
|
||||
).also(DslGradleRunner::setupProject)
|
||||
|
||||
@Test
|
||||
fun `configures detekt type resolution task main`() {
|
||||
val project = gradleRunner.buildProject()
|
||||
|
||||
val detektTask = project.tasks.getByPath("detektMain") as Detekt
|
||||
val argumentString = detektTask.arguments.get().joinToString(" ")
|
||||
|
||||
assertThat(argumentString).containsPattern("""--baseline \S*[/\\]detekt-baseline-main.xml """)
|
||||
assertThat(argumentString).contains("--report xml:")
|
||||
assertThat(argumentString).contains("--report sarif:")
|
||||
assertThat(argumentString).doesNotContain("--report txt:")
|
||||
assertThat(argumentString).contains("--classpath")
|
||||
val gradleRunner = DslGradleRunner(
|
||||
projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 1),
|
||||
buildFileName = "build.gradle.kts",
|
||||
baselineFiles = listOf("detekt-baseline.xml", "detekt-baseline-main.xml", "detekt-baseline-test.xml"),
|
||||
projectScript = {
|
||||
apply<KotlinPluginWrapper>()
|
||||
apply<DetektPlugin>()
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `configures detekt type resolution task test`() {
|
||||
val project = gradleRunner.buildProject()
|
||||
|
||||
val detektTask = project.tasks.getByPath("detektTest") as Detekt
|
||||
val argumentString = detektTask.arguments.get().joinToString(" ")
|
||||
|
||||
assertThat(argumentString).containsPattern("""--baseline \S*[/\\]detekt-baseline-test.xml """)
|
||||
assertThat(argumentString).contains("--report xml:")
|
||||
assertThat(argumentString).contains("--report sarif:")
|
||||
assertThat(argumentString).doesNotContain("--report txt:")
|
||||
assertThat(argumentString).contains("--classpath")
|
||||
tasks.withType(Detekt::class.java).configureEach {
|
||||
it.reports { reports ->
|
||||
reports.txt.required.set(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
).also(DslGradleRunner::setupProject)
|
||||
|
||||
@Test
|
||||
fun `configures detekt type resolution task main`() {
|
||||
val project = gradleRunner.buildProject()
|
||||
|
||||
val detektTask = project.tasks.getByPath("detektMain") as Detekt
|
||||
val argumentString = detektTask.arguments.get().joinToString(" ")
|
||||
|
||||
assertThat(argumentString).containsPattern("""--baseline \S*[/\\]detekt-baseline-main.xml """)
|
||||
assertThat(argumentString).contains("--report xml:")
|
||||
assertThat(argumentString).contains("--report sarif:")
|
||||
assertThat(argumentString).doesNotContain("--report txt:")
|
||||
assertThat(argumentString).contains("--classpath")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `configures detekt type resolution task test`() {
|
||||
val project = gradleRunner.buildProject()
|
||||
|
||||
val detektTask = project.tasks.getByPath("detektTest") as Detekt
|
||||
val argumentString = detektTask.arguments.get().joinToString(" ")
|
||||
|
||||
assertThat(argumentString).containsPattern("""--baseline \S*[/\\]detekt-baseline-test.xml """)
|
||||
assertThat(argumentString).contains("--report xml:")
|
||||
assertThat(argumentString).contains("--report sarif:")
|
||||
assertThat(argumentString).doesNotContain("--report txt:")
|
||||
assertThat(argumentString).contains("--classpath")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package io.gitlab.arturbosch.detekt.internal
|
||||
import io.gitlab.arturbosch.detekt.gradle.TestFileCollection
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.gradle.api.internal.file.AbstractFileCollection
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.File
|
||||
import java.util.concurrent.CompletableFuture
|
||||
@@ -14,66 +13,62 @@ import java.util.function.Supplier
|
||||
|
||||
class ClassLoaderCacheSpec {
|
||||
|
||||
@Nested
|
||||
inner class ClasspathChanges {
|
||||
@Test
|
||||
fun `same classloader is returned for the same files`() {
|
||||
val cache = DefaultClassLoaderCache()
|
||||
val initialClassLoader = cache.getOrCreate(TestFileCollection(File("a/b/c")))
|
||||
val secondClassLoader = cache.getOrCreate(TestFileCollection(File("a/b/c")))
|
||||
|
||||
@Test
|
||||
fun `same classloader is returned for the same files`() {
|
||||
val cache = DefaultClassLoaderCache()
|
||||
val initialClassLoader = cache.getOrCreate(TestFileCollection(File("a/b/c")))
|
||||
val secondClassLoader = cache.getOrCreate(TestFileCollection(File("a/b/c")))
|
||||
assertThat(initialClassLoader === secondClassLoader).isTrue()
|
||||
}
|
||||
|
||||
assertThat(initialClassLoader === secondClassLoader).isTrue()
|
||||
}
|
||||
@Test
|
||||
fun `different classloaders are returned for different files`() {
|
||||
val cache = DefaultClassLoaderCache()
|
||||
val firstClassLoader = cache.getOrCreate(TestFileCollection(File("a/b/c")))
|
||||
val secondClassLoader = cache.getOrCreate(TestFileCollection(File("c/b/a")))
|
||||
|
||||
@Test
|
||||
fun `different classloaders are returned for different files`() {
|
||||
val cache = DefaultClassLoaderCache()
|
||||
val firstClassLoader = cache.getOrCreate(TestFileCollection(File("a/b/c")))
|
||||
val secondClassLoader = cache.getOrCreate(TestFileCollection(File("c/b/a")))
|
||||
assertThat(firstClassLoader === secondClassLoader).isFalse()
|
||||
}
|
||||
|
||||
assertThat(firstClassLoader === secondClassLoader).isFalse()
|
||||
}
|
||||
@Test
|
||||
fun `same classloader for the same files in different order`() {
|
||||
val cache = DefaultClassLoaderCache()
|
||||
val firstClassLoader = cache.getOrCreate(TestFileCollection(File("a/b/c"), File("d/e/f")))
|
||||
val secondClassLoader = cache.getOrCreate(TestFileCollection(File("d/e/f"), File("a/b/c")))
|
||||
|
||||
@Test
|
||||
fun `same classloader for the same files in different order`() {
|
||||
val cache = DefaultClassLoaderCache()
|
||||
val firstClassLoader = cache.getOrCreate(TestFileCollection(File("a/b/c"), File("d/e/f")))
|
||||
val secondClassLoader = cache.getOrCreate(TestFileCollection(File("d/e/f"), File("a/b/c")))
|
||||
assertThat(firstClassLoader === secondClassLoader).isTrue()
|
||||
}
|
||||
|
||||
assertThat(firstClassLoader === secondClassLoader).isTrue()
|
||||
}
|
||||
@Test
|
||||
fun `resolves files without synchronization`() {
|
||||
val file1 = File("/a/b/c")
|
||||
val collection1 = CountdownFileCollection(file1)
|
||||
|
||||
@Test
|
||||
fun `resolves files without synchronization`() {
|
||||
val file1 = File("/a/b/c")
|
||||
val collection1 = CountdownFileCollection(file1)
|
||||
val file2 = File("/c/b/a")
|
||||
val collection2 = TestFileCollection(file2)
|
||||
|
||||
val file2 = File("/c/b/a")
|
||||
val collection2 = TestFileCollection(file2)
|
||||
|
||||
val cache = DefaultClassLoaderCache()
|
||||
val executor = Executors.newSingleThreadExecutor()
|
||||
val latch = CountDownLatch(1)
|
||||
try {
|
||||
val supplier = Supplier {
|
||||
latch.countDown()
|
||||
cache.getOrCreate(collection1)
|
||||
}
|
||||
val task = CompletableFuture.supplyAsync(supplier, executor)
|
||||
@Suppress("UsePropertyAccessSyntax")
|
||||
assertThat(latch.await(10L, TimeUnit.SECONDS)).isTrue()
|
||||
// Will call `getOrCreate` next - wait a moment to be sure
|
||||
Thread.sleep(2000L)
|
||||
val classpath2 = cache.getOrCreate(collection2)
|
||||
collection1.latch.countDown()
|
||||
val classpath1 = task.join()
|
||||
assertThat(classpath1.urLs).isEqualTo(arrayOf(file1.toURI().toURL()))
|
||||
assertThat(classpath2.urLs).isEqualTo(arrayOf(file2.toURI().toURL()))
|
||||
} finally {
|
||||
val remaining = executor.shutdownNow()
|
||||
assertThat(remaining).isEmpty()
|
||||
val cache = DefaultClassLoaderCache()
|
||||
val executor = Executors.newSingleThreadExecutor()
|
||||
val latch = CountDownLatch(1)
|
||||
try {
|
||||
val supplier = Supplier {
|
||||
latch.countDown()
|
||||
cache.getOrCreate(collection1)
|
||||
}
|
||||
val task = CompletableFuture.supplyAsync(supplier, executor)
|
||||
@Suppress("UsePropertyAccessSyntax")
|
||||
assertThat(latch.await(10L, TimeUnit.SECONDS)).isTrue()
|
||||
// Will call `getOrCreate` next - wait a moment to be sure
|
||||
Thread.sleep(2000L)
|
||||
val classpath2 = cache.getOrCreate(collection2)
|
||||
collection1.latch.countDown()
|
||||
val classpath1 = task.join()
|
||||
assertThat(classpath1.urLs).isEqualTo(arrayOf(file1.toURI().toURL()))
|
||||
assertThat(classpath2.urLs).isEqualTo(arrayOf(file2.toURI().toURL()))
|
||||
} finally {
|
||||
val remaining = executor.shutdownNow()
|
||||
assertThat(remaining).isEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.gitlab.arturbosch.detekt.report
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.File
|
||||
|
||||
@@ -9,49 +8,45 @@ private const val TAB = "\t"
|
||||
|
||||
class XmlReportMergerSpec {
|
||||
|
||||
@Nested
|
||||
inner class `classpath changes` {
|
||||
|
||||
@Test
|
||||
fun `passes for same files`() {
|
||||
val file1 = File.createTempFile("detekt1", "xml").apply {
|
||||
writeText(
|
||||
"""
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<checkstyle version="4.3">
|
||||
<file name="Sample1.kt">
|
||||
$TAB<error line="1" column="1" severity="warning" message="TestMessage" source="detekt.id_a" />
|
||||
</file>
|
||||
</checkstyle>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
val file2 = File.createTempFile("detekt2", "xml").apply {
|
||||
writeText(
|
||||
"""
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<checkstyle version="4.3">
|
||||
<file name="Sample2.kt">
|
||||
$TAB<error line="1" column="1" severity="warning" message="TestMessage" source="detekt.id_b" />
|
||||
</file>
|
||||
</checkstyle>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
val output = File.createTempFile("output", "xml")
|
||||
XmlReportMerger.merge(setOf(file1, file2), output)
|
||||
|
||||
val expectedText = """
|
||||
<?xml version="1.0" encoding="UTF-8"?><checkstyle version="4.3">
|
||||
<file name="Sample1.kt">
|
||||
<error column="1" line="1" message="TestMessage" severity="warning" source="detekt.id_a"/>
|
||||
</file>
|
||||
<file name="Sample2.kt">
|
||||
<error column="1" line="1" message="TestMessage" severity="warning" source="detekt.id_b"/>
|
||||
</file>
|
||||
@Test
|
||||
fun `passes for same files`() {
|
||||
val file1 = File.createTempFile("detekt1", "xml").apply {
|
||||
writeText(
|
||||
"""
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<checkstyle version="4.3">
|
||||
<file name="Sample1.kt">
|
||||
$TAB<error line="1" column="1" severity="warning" message="TestMessage" source="detekt.id_a" />
|
||||
</file>
|
||||
</checkstyle>
|
||||
""".trimIndent()
|
||||
assertThat(output.readText()).isEqualToIgnoringNewLines(expectedText)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
val file2 = File.createTempFile("detekt2", "xml").apply {
|
||||
writeText(
|
||||
"""
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<checkstyle version="4.3">
|
||||
<file name="Sample2.kt">
|
||||
$TAB<error line="1" column="1" severity="warning" message="TestMessage" source="detekt.id_b" />
|
||||
</file>
|
||||
</checkstyle>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
val output = File.createTempFile("output", "xml")
|
||||
XmlReportMerger.merge(setOf(file1, file2), output)
|
||||
|
||||
val expectedText = """
|
||||
<?xml version="1.0" encoding="UTF-8"?><checkstyle version="4.3">
|
||||
<file name="Sample1.kt">
|
||||
<error column="1" line="1" message="TestMessage" severity="warning" source="detekt.id_a"/>
|
||||
</file>
|
||||
<file name="Sample2.kt">
|
||||
<error column="1" line="1" message="TestMessage" severity="warning" source="detekt.id_b"/>
|
||||
</file>
|
||||
</checkstyle>
|
||||
""".trimIndent()
|
||||
assertThat(output.readText()).isEqualToIgnoringNewLines(expectedText)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,282 +7,278 @@ import org.junit.jupiter.api.Test
|
||||
|
||||
class CognitiveComplexitySpec {
|
||||
|
||||
@Test
|
||||
fun `sums seven for sumOfPrimes example`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun sumOfPrimes(max: Int): Int {
|
||||
var total = 0
|
||||
next@ for (i in 1..max) {
|
||||
for (j in 2 until i) {
|
||||
if (i % j == 0) {
|
||||
continue@next
|
||||
}
|
||||
}
|
||||
println(i)
|
||||
total++
|
||||
}
|
||||
return total
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(7)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sums one for getWords example for a single when expression`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun getWords(number: Int): String = when (number) {
|
||||
1 -> "one"
|
||||
2 -> "a couple"
|
||||
3 -> "a few"
|
||||
else -> "lots"
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(1)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `cognitive complexity` {
|
||||
inner class `recursion` {
|
||||
|
||||
@Test
|
||||
fun `sums seven for sumOfPrimes example`() {
|
||||
fun `adds one for recursion inside class`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun sumOfPrimes(max: Int): Int {
|
||||
var total = 0
|
||||
next@ for (i in 1..max) {
|
||||
for (j in 2 until i) {
|
||||
if (i % j == 0) {
|
||||
continue@next
|
||||
}
|
||||
}
|
||||
println(i)
|
||||
total++
|
||||
}
|
||||
return total
|
||||
class A {
|
||||
fun factorial(n: Int): Int =
|
||||
if (n >= 1) n * this.factorial(n - 1) else 1
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(7)
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sums one for getWords example for a single when expression`() {
|
||||
fun `adds one for top level recursion`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun getWords(number: Int): String = when (number) {
|
||||
1 -> "one"
|
||||
2 -> "a couple"
|
||||
3 -> "a few"
|
||||
else -> "lots"
|
||||
}
|
||||
fun factorial(n: Int): Int =
|
||||
if (n >= 1) n * factorial(n - 1) else 1
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not add as it is only the same name`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
object O { fun factorial(i: Int): Int = i - 1 }
|
||||
fun factorial(n: Int): Int =
|
||||
if (n >= 1) n * O.factorial(n - 1) else 1
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `recursion` {
|
||||
@Test
|
||||
fun `ignores shorthand operators`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun parse(args: Array<String>): Nothing = TODO()
|
||||
fun main(args: Array<String>) {
|
||||
args.takeIf { it.size > 3 }?.let(::parse) ?: error("not enough arguments")
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `adds one for recursion inside class`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
class A {
|
||||
fun factorial(n: Int): Int =
|
||||
if (n >= 1) n * this.factorial(n - 1) else 1
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `adds one per catch clause`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun main() {
|
||||
try {
|
||||
} catch (e: IllegalArgumentException) {
|
||||
} catch (e: IllegalStateException) {
|
||||
} catch (e: Throwable) {}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `adds extra complexity for nesting`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun main() {
|
||||
try {
|
||||
if (true) { // +1
|
||||
for (i in 0..10) { // +2
|
||||
while(true) { // +3
|
||||
// more code
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(2)
|
||||
} catch (e: Exception) { // +1
|
||||
do {} while(true) // +2
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `adds one for top level recursion`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun factorial(n: Int): Int =
|
||||
if (n >= 1) n * factorial(n - 1) else 1
|
||||
"""
|
||||
)
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(9)
|
||||
}
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(2)
|
||||
}
|
||||
@Test
|
||||
fun `adds nesting for lambdas but not complexity`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun main() { run { if (true) {} } }
|
||||
"""
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `does not add as it is only the same name`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
object O { fun factorial(i: Int): Int = i - 1 }
|
||||
fun factorial(n: Int): Int =
|
||||
if (n >= 1) n * O.factorial(n - 1) else 1
|
||||
"""
|
||||
)
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(2)
|
||||
}
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(1)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun `adds nesting for nested functions but not complexity`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun main() { fun run() { if (true) {} } }
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(2)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `binary expressions` {
|
||||
|
||||
@Test
|
||||
fun `ignores shorthand operators`() {
|
||||
fun `does not increment on just a condition`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun parse(args: Array<String>): Nothing = TODO()
|
||||
fun main(args: Array<String>) {
|
||||
args.takeIf { it.size > 3 }?.let(::parse) ?: error("not enough arguments")
|
||||
}
|
||||
fun test(cond_ Boolean) = !cond
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `adds one per catch clause`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun main() {
|
||||
try {
|
||||
} catch (e: IllegalArgumentException) {
|
||||
} catch (e: IllegalStateException) {
|
||||
} catch (e: Throwable) {}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `adds extra complexity for nesting`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun main() {
|
||||
try {
|
||||
if (true) { // +1
|
||||
for (i in 0..10) { // +2
|
||||
while(true) { // +3
|
||||
// more code
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) { // +1
|
||||
do {} while(true) // +2
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(9)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `adds nesting for lambdas but not complexity`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun main() { run { if (true) {} } }
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `adds nesting for nested functions but not complexity`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun main() { fun run() { if (true) {} } }
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(2)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `binary expressions` {
|
||||
inner class `increments for every non-like operator` {
|
||||
|
||||
@Test
|
||||
fun `does not increment on just a condition`() {
|
||||
fun `adds one for just a &&`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun test(cond_ Boolean) = !cond
|
||||
fun test(cond_ Boolean) = !cond && !cond
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(0)
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(1)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `increments for every non-like operator` {
|
||||
@Test
|
||||
fun `adds only one for repeated &&`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun test(cond_ Boolean) = !cond && !cond && !cond
|
||||
"""
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `adds one for just a &&`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun test(cond_ Boolean) = !cond && !cond
|
||||
"""
|
||||
)
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(1)
|
||||
}
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(1)
|
||||
}
|
||||
@Test
|
||||
fun `adds one per logical alternate operator`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun test(cond_ Boolean) = !cond && !cond || cond
|
||||
"""
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `adds only one for repeated &&`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun test(cond_ Boolean) = !cond && !cond && !cond
|
||||
"""
|
||||
)
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(2)
|
||||
}
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(1)
|
||||
}
|
||||
@Test
|
||||
fun `adds one per logical alternate operator with like operators in between`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun test(cond_ Boolean) {
|
||||
if ( // +1
|
||||
!cond
|
||||
&& !cond && !cond // +1
|
||||
|| cond || cond // +1
|
||||
&& cond // +1
|
||||
) {}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `adds one per logical alternate operator`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun test(cond_ Boolean) = !cond && !cond || cond
|
||||
"""
|
||||
)
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(4)
|
||||
}
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(2)
|
||||
}
|
||||
@Test
|
||||
fun `adds one for negated but similar operators`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun test(cond_ Boolean) {
|
||||
if ( // +1
|
||||
!cond
|
||||
&& !(cond && cond) // +2
|
||||
) {}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `adds one per logical alternate operator with like operators in between`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun test(cond_ Boolean) {
|
||||
if ( // +1
|
||||
!cond
|
||||
&& !cond && !cond // +1
|
||||
|| cond || cond // +1
|
||||
&& cond // +1
|
||||
) {}
|
||||
}
|
||||
"""
|
||||
)
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(3)
|
||||
}
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(4)
|
||||
}
|
||||
@Test
|
||||
fun `adds only one for a negated chain of similar operators`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun test(cond_ Boolean) {
|
||||
if ( // +1
|
||||
!cond
|
||||
&& !(cond && cond && cond) // +2
|
||||
) {}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `adds one for negated but similar operators`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun test(cond_ Boolean) {
|
||||
if ( // +1
|
||||
!cond
|
||||
&& !(cond && cond) // +2
|
||||
) {}
|
||||
}
|
||||
"""
|
||||
)
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(3)
|
||||
}
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(3)
|
||||
}
|
||||
@Test
|
||||
fun `adds one for every negated similar operator chain`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun test(cond_ Boolean) {
|
||||
if ( // +1
|
||||
!cond
|
||||
&& !(cond && cond && cond) // +2
|
||||
|| !(cond || cond) // +2
|
||||
) {}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `adds only one for a negated chain of similar operators`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun test(cond_ Boolean) {
|
||||
if ( // +1
|
||||
!cond
|
||||
&& !(cond && cond && cond) // +2
|
||||
) {}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `adds one for every negated similar operator chain`() {
|
||||
val code = compileContentForTest(
|
||||
"""
|
||||
fun test(cond_ Boolean) {
|
||||
if ( // +1
|
||||
!cond
|
||||
&& !(cond && cond && cond) // +2
|
||||
|| !(cond || cond) // +2
|
||||
) {}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(5)
|
||||
}
|
||||
assertThat(CognitiveComplexity.calculate(code)).isEqualTo(5)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,72 +17,68 @@ import org.junit.jupiter.api.Test
|
||||
|
||||
internal class ComplexityReportGeneratorSpec {
|
||||
|
||||
private lateinit var detektion: TestDetektion
|
||||
|
||||
@BeforeEach
|
||||
fun setupMocks() {
|
||||
val finding = mockk<Finding>()
|
||||
every { finding.id }.returns("test")
|
||||
detektion = TestDetektion(finding).withTestData()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `complexity report generator` {
|
||||
inner class `several complexity metrics` {
|
||||
|
||||
private lateinit var detektion: TestDetektion
|
||||
@Test
|
||||
fun `successfully generates a complexity report`() {
|
||||
val expectedContent = listOf(
|
||||
"1,000 lines of code (loc)",
|
||||
"6 source lines of code (sloc)",
|
||||
"5 logical lines of code (lloc)",
|
||||
"4 comment lines of code (cloc)",
|
||||
"2 cyclomatic complexity (mcc)",
|
||||
"2 cognitive complexity",
|
||||
"1 number of total code smells",
|
||||
"66% comment source ratio",
|
||||
"400 mcc per 1,000 lloc",
|
||||
"200 code smells per 1,000 lloc"
|
||||
)
|
||||
|
||||
@BeforeEach
|
||||
fun setupMocks() {
|
||||
val finding = mockk<Finding>()
|
||||
every { finding.id }.returns("test")
|
||||
detektion = TestDetektion(finding).withTestData()
|
||||
assertThat(generateComplexityReport(detektion)).isEqualTo(expectedContent)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `several invalid complexity metrics` {
|
||||
|
||||
@Test
|
||||
fun `returns null for missing mcc`() {
|
||||
detektion.removeData(complexityKey)
|
||||
assertThat(generateComplexityReport(detektion)).isNull()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `several complexity metrics` {
|
||||
@Test
|
||||
fun `returns null for missing lloc`() {
|
||||
detektion.removeData(logicalLinesKey)
|
||||
assertThat(generateComplexityReport(detektion)).isNull()
|
||||
|
||||
@Test
|
||||
fun `successfully generates a complexity report`() {
|
||||
val expectedContent = listOf(
|
||||
"1,000 lines of code (loc)",
|
||||
"6 source lines of code (sloc)",
|
||||
"5 logical lines of code (lloc)",
|
||||
"4 comment lines of code (cloc)",
|
||||
"2 cyclomatic complexity (mcc)",
|
||||
"2 cognitive complexity",
|
||||
"1 number of total code smells",
|
||||
"66% comment source ratio",
|
||||
"400 mcc per 1,000 lloc",
|
||||
"200 code smells per 1,000 lloc"
|
||||
)
|
||||
|
||||
assertThat(generateComplexityReport(detektion)).isEqualTo(expectedContent)
|
||||
}
|
||||
detektion.addData(logicalLinesKey, 0)
|
||||
assertThat(generateComplexityReport(detektion)).isNull()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `several invalid complexity metrics` {
|
||||
@Test
|
||||
fun `returns null for missing sloc`() {
|
||||
detektion.removeData(sourceLinesKey)
|
||||
assertThat(generateComplexityReport(detektion)).isNull()
|
||||
|
||||
@Test
|
||||
fun `returns null for missing mcc`() {
|
||||
detektion.removeData(complexityKey)
|
||||
assertThat(generateComplexityReport(detektion)).isNull()
|
||||
}
|
||||
detektion.addData(sourceLinesKey, 0)
|
||||
assertThat(generateComplexityReport(detektion)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns null for missing lloc`() {
|
||||
detektion.removeData(logicalLinesKey)
|
||||
assertThat(generateComplexityReport(detektion)).isNull()
|
||||
|
||||
detektion.addData(logicalLinesKey, 0)
|
||||
assertThat(generateComplexityReport(detektion)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns null for missing sloc`() {
|
||||
detektion.removeData(sourceLinesKey)
|
||||
assertThat(generateComplexityReport(detektion)).isNull()
|
||||
|
||||
detektion.addData(sourceLinesKey, 0)
|
||||
assertThat(generateComplexityReport(detektion)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `returns null for missing cloc`() {
|
||||
detektion.removeData(complexityKey)
|
||||
assertThat(generateComplexityReport(detektion)).isNull()
|
||||
}
|
||||
@Test
|
||||
fun `returns null for missing cloc`() {
|
||||
detektion.removeData(complexityKey)
|
||||
assertThat(generateComplexityReport(detektion)).isNull()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,21 +2,17 @@ package io.github.detekt.metrics.processors
|
||||
|
||||
import io.github.detekt.test.utils.compileContentForTest
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class CLOCVisitorSpec {
|
||||
@Nested
|
||||
inner class `CLOC` {
|
||||
|
||||
@Test
|
||||
fun `commentCases`() {
|
||||
val file = compileContentForTest(commentsClass)
|
||||
val commentLines = with(file) {
|
||||
accept(CLOCVisitor())
|
||||
getUserData(commentLinesKey)
|
||||
}
|
||||
assertThat(commentLines).isEqualTo(10)
|
||||
@Test
|
||||
fun `commentCases`() {
|
||||
val file = compileContentForTest(commentsClass)
|
||||
val commentLines = with(file) {
|
||||
accept(CLOCVisitor())
|
||||
getUserData(commentLinesKey)
|
||||
}
|
||||
assertThat(commentLines).isEqualTo(10)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,39 +3,34 @@ package io.github.detekt.metrics.processors
|
||||
import io.github.detekt.test.utils.compileContentForTest
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class ClassCountVisitorSpec {
|
||||
@Nested
|
||||
inner class `something` {
|
||||
@Test
|
||||
fun `twoClassesInSeparateFile`() {
|
||||
val files = arrayOf(
|
||||
compileContentForTest(default),
|
||||
compileContentForTest(classWithFields)
|
||||
)
|
||||
val count = getClassCount(files)
|
||||
assertThat(count).isEqualTo(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `twoClassesInSeparateFile`() {
|
||||
val files = arrayOf(
|
||||
compileContentForTest(default),
|
||||
compileContentForTest(classWithFields)
|
||||
)
|
||||
val count = getClassCount(files)
|
||||
assertThat(count).isEqualTo(2)
|
||||
}
|
||||
@Test
|
||||
fun `oneClassWithOneNestedClass`() {
|
||||
val file = compileContentForTest(complexClass)
|
||||
val count = getClassCount(arrayOf(file))
|
||||
assertThat(count).isEqualTo(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `oneClassWithOneNestedClass`() {
|
||||
val file = compileContentForTest(complexClass)
|
||||
val count = getClassCount(arrayOf(file))
|
||||
assertThat(count).isEqualTo(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `testEnumAndInterface`() {
|
||||
val files = arrayOf(
|
||||
compileContentForTest(emptyEnum),
|
||||
compileContentForTest(emptyInterface)
|
||||
)
|
||||
val count = getClassCount(files)
|
||||
assertThat(count).isEqualTo(2)
|
||||
}
|
||||
@Test
|
||||
fun `testEnumAndInterface`() {
|
||||
val files = arrayOf(
|
||||
compileContentForTest(emptyEnum),
|
||||
compileContentForTest(emptyInterface)
|
||||
)
|
||||
val count = getClassCount(files)
|
||||
assertThat(count).isEqualTo(2)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,22 +3,17 @@ package io.github.detekt.metrics.processors
|
||||
import io.github.detekt.metrics.CognitiveComplexity
|
||||
import io.github.detekt.test.utils.compileContentForTest
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class CognitiveComplexityProcessorSpec {
|
||||
|
||||
@Nested
|
||||
inner class `CognitiveComplexityProcessor` {
|
||||
@Test
|
||||
fun `counts the complexity for the whole file`() {
|
||||
val file = compileContentForTest(complexClass)
|
||||
|
||||
@Test
|
||||
fun `counts the complexity for the whole file`() {
|
||||
val file = compileContentForTest(complexClass)
|
||||
val value = MetricProcessorTester(file)
|
||||
.test(ProjectCognitiveComplexityProcessor(), CognitiveComplexity.KEY)
|
||||
|
||||
val value = MetricProcessorTester(file)
|
||||
.test(ProjectCognitiveComplexityProcessor(), CognitiveComplexity.KEY)
|
||||
|
||||
assertThat(value).isEqualTo(46)
|
||||
}
|
||||
assertThat(value).isEqualTo(46)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,26 +2,21 @@ package io.github.detekt.metrics.processors
|
||||
|
||||
import io.github.detekt.test.utils.compileContentForTest
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class ComplexityVisitorSpec {
|
||||
@Nested
|
||||
inner class `something` {
|
||||
@Test
|
||||
fun `complexityOfDefaultCaseIsOne`() {
|
||||
val mcc = calcComplexity(default)
|
||||
|
||||
@Test
|
||||
fun `complexityOfDefaultCaseIsOne`() {
|
||||
val mcc = calcComplexity(default)
|
||||
assertThat(mcc).isEqualTo(0)
|
||||
}
|
||||
|
||||
assertThat(mcc).isEqualTo(0)
|
||||
}
|
||||
@Test
|
||||
fun `complexityOfComplexAndNestedClass`() {
|
||||
val mcc = calcComplexity(complexClass)
|
||||
|
||||
@Test
|
||||
fun `complexityOfComplexAndNestedClass`() {
|
||||
val mcc = calcComplexity(complexClass)
|
||||
|
||||
assertThat(mcc).isEqualTo(44)
|
||||
}
|
||||
assertThat(mcc).isEqualTo(44)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,21 +2,17 @@ package io.github.detekt.metrics.processors
|
||||
|
||||
import io.github.detekt.test.utils.compileContentForTest
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class FieldCountVisitorSpec {
|
||||
@Nested
|
||||
inner class `something` {
|
||||
|
||||
@Test
|
||||
fun `defaultFieldCount`() {
|
||||
val file = compileContentForTest(classWithFields)
|
||||
val count = with(file) {
|
||||
accept(PropertyCountVisitor())
|
||||
getUserData(numberOfFieldsKey)
|
||||
}
|
||||
assertThat(count).isEqualTo(2)
|
||||
@Test
|
||||
fun `defaultFieldCount`() {
|
||||
val file = compileContentForTest(classWithFields)
|
||||
val count = with(file) {
|
||||
accept(PropertyCountVisitor())
|
||||
getUserData(numberOfFieldsKey)
|
||||
}
|
||||
assertThat(count).isEqualTo(2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,22 +3,18 @@ package io.github.detekt.metrics.processors
|
||||
import io.github.detekt.test.utils.compileContentForTest
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class KtFileCountVisitorSpec {
|
||||
@Nested
|
||||
inner class `files` {
|
||||
|
||||
@Test
|
||||
fun `twoFiles`() {
|
||||
val files = arrayOf(
|
||||
compileContentForTest(default),
|
||||
compileContentForTest(complexClass)
|
||||
)
|
||||
val count = files.sumOf { getData(it) }
|
||||
assertThat(count).isEqualTo(2)
|
||||
}
|
||||
@Test
|
||||
fun `twoFiles`() {
|
||||
val files = arrayOf(
|
||||
compileContentForTest(default),
|
||||
compileContentForTest(complexClass)
|
||||
)
|
||||
val count = files.sumOf { getData(it) }
|
||||
assertThat(count).isEqualTo(2)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,35 +2,31 @@ package io.github.detekt.metrics.processors
|
||||
|
||||
import io.github.detekt.test.utils.compileContentForTest
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class LLOCVisitorSpec {
|
||||
@Nested
|
||||
inner class `LLOC Visitor` {
|
||||
|
||||
@Test
|
||||
fun `defaultCaseHasOneClassAndAnnotationLine`() {
|
||||
val file = compileContentForTest(default)
|
||||
@Test
|
||||
fun `defaultCaseHasOneClassAndAnnotationLine`() {
|
||||
val file = compileContentForTest(default)
|
||||
|
||||
val lloc = with(file) {
|
||||
accept(LLOCVisitor())
|
||||
getUserData(logicalLinesKey)
|
||||
}
|
||||
|
||||
assertThat(lloc).isEqualTo(2)
|
||||
val lloc = with(file) {
|
||||
accept(LLOCVisitor())
|
||||
getUserData(logicalLinesKey)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `llocOfComplexClass`() {
|
||||
val file = compileContentForTest(complexClass)
|
||||
assertThat(lloc).isEqualTo(2)
|
||||
}
|
||||
|
||||
val lloc = with(file) {
|
||||
accept(LLOCVisitor())
|
||||
getUserData(logicalLinesKey)
|
||||
}
|
||||
@Test
|
||||
fun `llocOfComplexClass`() {
|
||||
val file = compileContentForTest(complexClass)
|
||||
|
||||
assertThat(lloc).isEqualTo(85)
|
||||
val lloc = with(file) {
|
||||
accept(LLOCVisitor())
|
||||
getUserData(logicalLinesKey)
|
||||
}
|
||||
|
||||
assertThat(lloc).isEqualTo(85)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,21 +2,17 @@ package io.github.detekt.metrics.processors
|
||||
|
||||
import io.github.detekt.test.utils.compileContentForTest
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class LOCVisitorSpec {
|
||||
@Nested
|
||||
inner class `LOC Visitor` {
|
||||
|
||||
@Test
|
||||
fun `defaultClass`() {
|
||||
val file = compileContentForTest(default)
|
||||
val loc = with(file) {
|
||||
accept(LOCVisitor())
|
||||
getUserData(linesKey)
|
||||
}
|
||||
assertThat(loc).isEqualTo(8)
|
||||
@Test
|
||||
fun `defaultClass`() {
|
||||
val file = compileContentForTest(default)
|
||||
val loc = with(file) {
|
||||
accept(LOCVisitor())
|
||||
getUserData(linesKey)
|
||||
}
|
||||
assertThat(loc).isEqualTo(8)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,19 +3,15 @@ package io.github.detekt.metrics.processors
|
||||
import io.github.detekt.test.utils.compileContentForTest
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class MethodCountVisitorSpec {
|
||||
@Nested
|
||||
inner class `Method Count Visitor` {
|
||||
|
||||
@Test
|
||||
fun `defaultMethodCount`() {
|
||||
val file = compileContentForTest(complexClass)
|
||||
val count = getMethodCount(file)
|
||||
assertThat(count).isEqualTo(6)
|
||||
}
|
||||
@Test
|
||||
fun `defaultMethodCount`() {
|
||||
val file = compileContentForTest(complexClass)
|
||||
val count = getMethodCount(file)
|
||||
assertThat(count).isEqualTo(6)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,25 +3,21 @@ package io.github.detekt.metrics.processors
|
||||
import io.github.detekt.test.utils.compileContentForTest
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class PackageCountVisitorSpec {
|
||||
@Nested
|
||||
inner class `Package Count Visitor` {
|
||||
|
||||
@Test
|
||||
fun `twoClassesInSeparatePackage`() {
|
||||
val files = arrayOf(
|
||||
compileContentForTest(default),
|
||||
compileContentForTest(emptyEnum)
|
||||
)
|
||||
val count = files
|
||||
.map { getData(it) }
|
||||
.distinct()
|
||||
.count()
|
||||
assertThat(count).isEqualTo(2)
|
||||
}
|
||||
@Test
|
||||
fun `twoClassesInSeparatePackage`() {
|
||||
val files = arrayOf(
|
||||
compileContentForTest(default),
|
||||
compileContentForTest(emptyEnum)
|
||||
)
|
||||
val count = files
|
||||
.map { getData(it) }
|
||||
.distinct()
|
||||
.count()
|
||||
assertThat(count).isEqualTo(2)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,21 +2,17 @@ package io.github.detekt.metrics.processors
|
||||
|
||||
import io.github.detekt.test.utils.compileContentForTest
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class SLOCVisitorSpec {
|
||||
@Nested
|
||||
inner class `SLOC Visitor` {
|
||||
|
||||
@Test
|
||||
fun `defaultClass`() {
|
||||
val file = compileContentForTest(default)
|
||||
val loc = with(file) {
|
||||
accept(SLOCVisitor())
|
||||
getUserData(sourceLinesKey)
|
||||
}
|
||||
assertThat(loc).isEqualTo(3)
|
||||
@Test
|
||||
fun `defaultClass`() {
|
||||
val file = compileContentForTest(default)
|
||||
val loc = with(file) {
|
||||
accept(SLOCVisitor())
|
||||
getUserData(sourceLinesKey)
|
||||
}
|
||||
assertThat(loc).isEqualTo(3)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,26 @@
|
||||
package io.github.detekt.parser
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.File
|
||||
|
||||
class KotlinEnvironmentUtilsSpec {
|
||||
|
||||
@Nested
|
||||
inner class `retrieved kotlin language version` {
|
||||
@Test
|
||||
fun `should match`() {
|
||||
val expectedVersionString = System.getProperty("kotlinVersion", "")
|
||||
.splitToSequence('.')
|
||||
.take(2)
|
||||
.joinToString(".")
|
||||
@Test
|
||||
fun `retrieved kotlin language version should match`() {
|
||||
val expectedVersionString = System.getProperty("kotlinVersion", "")
|
||||
.splitToSequence('.')
|
||||
.take(2)
|
||||
.joinToString(".")
|
||||
|
||||
val classpathFiles = System.getProperty("testClasspath", "")
|
||||
.splitToSequence(';')
|
||||
.map(::File)
|
||||
.filter(File::exists)
|
||||
.toList()
|
||||
val classpathFiles = System.getProperty("testClasspath", "")
|
||||
.splitToSequence(';')
|
||||
.map(::File)
|
||||
.filter(File::exists)
|
||||
.toList()
|
||||
|
||||
val languageVersion = classpathFiles.getKotlinLanguageVersion()
|
||||
assertThat(languageVersion).isNotNull
|
||||
assertThat(languageVersion?.versionString).isEqualTo(expectedVersionString)
|
||||
}
|
||||
val languageVersion = classpathFiles.getKotlinLanguageVersion()
|
||||
assertThat(languageVersion).isNotNull
|
||||
assertThat(languageVersion?.versionString).isEqualTo(expectedVersionString)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,162 +21,157 @@ import io.mockk.mockk
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.jetbrains.kotlin.com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
class HtmlOutputReportSpec {
|
||||
|
||||
@Nested
|
||||
inner class `HTML output report` {
|
||||
private val htmlReport = HtmlOutputReport()
|
||||
|
||||
private val htmlReport = HtmlOutputReport()
|
||||
@Test
|
||||
fun `renders the HTML headers correctly`() {
|
||||
val result = htmlReport.render(TestDetektion())
|
||||
|
||||
@Test
|
||||
fun `renders the HTML headers correctly`() {
|
||||
val result = htmlReport.render(TestDetektion())
|
||||
assertThat(result).startsWith("<!DOCTYPE html>\n<html lang=\"en\">")
|
||||
assertThat(result).endsWith("</html>\n")
|
||||
|
||||
assertThat(result).startsWith("<!DOCTYPE html>\n<html lang=\"en\">")
|
||||
assertThat(result).endsWith("</html>\n")
|
||||
assertThat(result).contains("<h2>Metrics</h2>")
|
||||
assertThat(result).contains("<h2>Complexity Report</h2>")
|
||||
assertThat(result).contains("<h2>Findings</h2>")
|
||||
}
|
||||
|
||||
assertThat(result).contains("<h2>Metrics</h2>")
|
||||
assertThat(result).contains("<h2>Complexity Report</h2>")
|
||||
assertThat(result).contains("<h2>Findings</h2>")
|
||||
@Test
|
||||
fun `renders the 'generated with' text correctly`() {
|
||||
val version = whichDetekt()
|
||||
val header =
|
||||
"""generated with <a href="https://detekt.dev/">detekt version $version</a> on """
|
||||
|
||||
val result = htmlReport.render(TestDetektion())
|
||||
|
||||
assertThat(result).contains(header)
|
||||
assertThat(result).doesNotContain("@@@date@@@")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `contains the total number of findings`() {
|
||||
val result = htmlReport.render(createTestDetektionWithMultipleSmells())
|
||||
|
||||
assertThat(result).contains("Total: 3")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `contains no findings`() {
|
||||
val detektion = object : TestDetektion() {
|
||||
override val findings: Map<String, List<Finding>> = mapOf(
|
||||
"EmptyRuleset" to emptyList()
|
||||
)
|
||||
}
|
||||
val result = htmlReport.render(detektion)
|
||||
assertThat(result).contains("Total: 0")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders the 'generated with' text correctly`() {
|
||||
val version = whichDetekt()
|
||||
val header =
|
||||
"""generated with <a href="https://detekt.dev/">detekt version $version</a> on """
|
||||
@Test
|
||||
fun `renders the right file locations`() {
|
||||
val result = htmlReport.render(createTestDetektionWithMultipleSmells())
|
||||
|
||||
val result = htmlReport.render(TestDetektion())
|
||||
assertThat(result).contains("<span class=\"location\">src/main/com/sample/Sample1.kt:11:1</span>")
|
||||
assertThat(result).contains("<span class=\"location\">src/main/com/sample/Sample2.kt:22:2</span>")
|
||||
assertThat(result).contains("<span class=\"location\">src/main/com/sample/Sample3.kt:33:3</span>")
|
||||
}
|
||||
|
||||
assertThat(result).contains(header)
|
||||
assertThat(result).doesNotContain("@@@date@@@")
|
||||
@Test
|
||||
fun `renders the right file locations for relative paths`() {
|
||||
val result = htmlReport.render(createTestDetektionFromRelativePath())
|
||||
|
||||
assertThat(result).contains("<span class=\"location\">src/main/com/sample/Sample1.kt:11:1</span>")
|
||||
assertThat(result).contains("<span class=\"location\">src/main/com/sample/Sample2.kt:22:2</span>")
|
||||
assertThat(result).contains("<span class=\"location\">src/main/com/sample/Sample3.kt:33:3</span>")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders the right number of issues per rule`() {
|
||||
val result = htmlReport.render(createTestDetektionWithMultipleSmells())
|
||||
|
||||
assertThat(result).contains("<span class=\"rule\">id_a: 2 </span>")
|
||||
assertThat(result).contains("<span class=\"rule\">id_b: 1 </span>")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders the right violation messages for the rules`() {
|
||||
val result = htmlReport.render(createTestDetektionWithMultipleSmells())
|
||||
|
||||
assertThat(result).contains("<span class=\"message\">Message finding 1</span>")
|
||||
assertThat(result).contains("<span class=\"message\">Message finding 2</span>")
|
||||
assertThat(result).doesNotContain("<span class=\"message\"></span>")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders the right violation description for the rules`() {
|
||||
val result = htmlReport.render(createTestDetektionWithMultipleSmells())
|
||||
|
||||
assertThat(result).contains("<span class=\"description\">Description id_a</span>")
|
||||
assertThat(result).contains("<span class=\"description\">Description id_b</span>")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders a metric report correctly`() {
|
||||
val detektion = object : TestDetektion() {
|
||||
override val metrics: Collection<ProjectMetric> = listOf(
|
||||
ProjectMetric("M1", 10_000),
|
||||
ProjectMetric("M2", 2)
|
||||
)
|
||||
}
|
||||
val result = htmlReport.render(detektion)
|
||||
assertThat(result).contains("<li>10,000 M1</li>")
|
||||
assertThat(result).contains("<li>2 M2</li>")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `contains the total number of findings`() {
|
||||
val result = htmlReport.render(createTestDetektionWithMultipleSmells())
|
||||
@Test
|
||||
fun `renders the complexity report correctly`() {
|
||||
val detektion = TestDetektion()
|
||||
detektion.addData(complexityKey, 10)
|
||||
detektion.addData(CognitiveComplexity.KEY, 10)
|
||||
detektion.addData(sourceLinesKey, 20)
|
||||
detektion.addData(logicalLinesKey, 10)
|
||||
detektion.addData(commentLinesKey, 2)
|
||||
detektion.addData(linesKey, 2222)
|
||||
val result = htmlReport.render(detektion)
|
||||
assertThat(result).contains("<li>2,222 lines of code (loc)</li>")
|
||||
assertThat(result).contains("<li>20 source lines of code (sloc)</li>")
|
||||
assertThat(result).contains("<li>10 logical lines of code (lloc)</li>")
|
||||
}
|
||||
|
||||
assertThat(result).contains("Total: 3")
|
||||
}
|
||||
@Test
|
||||
fun `renders a blank complexity report correctly`() {
|
||||
val result = htmlReport.render(createTestDetektionWithMultipleSmells())
|
||||
assertThat(result).contains("<h2>Complexity Report</h2>\n\n<div>\n <ul></ul>\n</div>")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `contains no findings`() {
|
||||
val detektion = object : TestDetektion() {
|
||||
override val findings: Map<String, List<Finding>> = mapOf(
|
||||
"EmptyRuleset" to emptyList()
|
||||
)
|
||||
}
|
||||
val result = htmlReport.render(detektion)
|
||||
assertThat(result).contains("Total: 0")
|
||||
}
|
||||
@Test
|
||||
fun `asserts that the generated HTML is the same as expected`() {
|
||||
val expected = resourceAsPath("HtmlOutputFormatTest.html")
|
||||
var result = htmlReport.render(createTestDetektionWithMultipleSmells())
|
||||
result = generatedRegex.replace(result, replacement)
|
||||
|
||||
@Test
|
||||
fun `renders the right file locations`() {
|
||||
val result = htmlReport.render(createTestDetektionWithMultipleSmells())
|
||||
val actual = createTempFileForTest("actual-report", ".html")
|
||||
Files.write(actual, result.toByteArray())
|
||||
|
||||
assertThat(result).contains("<span class=\"location\">src/main/com/sample/Sample1.kt:11:1</span>")
|
||||
assertThat(result).contains("<span class=\"location\">src/main/com/sample/Sample2.kt:22:2</span>")
|
||||
assertThat(result).contains("<span class=\"location\">src/main/com/sample/Sample3.kt:33:3</span>")
|
||||
}
|
||||
assertThat(actual).hasSameTextualContentAs(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders the right file locations for relative paths`() {
|
||||
val result = htmlReport.render(createTestDetektionFromRelativePath())
|
||||
@Test
|
||||
fun `asserts that the generated HTML is the same even if we change the order of the findings`() {
|
||||
val findings = findings()
|
||||
val reversedFindings = findings
|
||||
.reversedArray()
|
||||
.map { (section, findings) -> section to findings.asReversed() }
|
||||
.toTypedArray()
|
||||
|
||||
assertThat(result).contains("<span class=\"location\">src/main/com/sample/Sample1.kt:11:1</span>")
|
||||
assertThat(result).contains("<span class=\"location\">src/main/com/sample/Sample2.kt:22:2</span>")
|
||||
assertThat(result).contains("<span class=\"location\">src/main/com/sample/Sample3.kt:33:3</span>")
|
||||
}
|
||||
val firstReport = createReportWithFindings(findings)
|
||||
val secondReport = createReportWithFindings(reversedFindings)
|
||||
|
||||
@Test
|
||||
fun `renders the right number of issues per rule`() {
|
||||
val result = htmlReport.render(createTestDetektionWithMultipleSmells())
|
||||
|
||||
assertThat(result).contains("<span class=\"rule\">id_a: 2 </span>")
|
||||
assertThat(result).contains("<span class=\"rule\">id_b: 1 </span>")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders the right violation messages for the rules`() {
|
||||
val result = htmlReport.render(createTestDetektionWithMultipleSmells())
|
||||
|
||||
assertThat(result).contains("<span class=\"message\">Message finding 1</span>")
|
||||
assertThat(result).contains("<span class=\"message\">Message finding 2</span>")
|
||||
assertThat(result).doesNotContain("<span class=\"message\"></span>")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders the right violation description for the rules`() {
|
||||
val result = htmlReport.render(createTestDetektionWithMultipleSmells())
|
||||
|
||||
assertThat(result).contains("<span class=\"description\">Description id_a</span>")
|
||||
assertThat(result).contains("<span class=\"description\">Description id_b</span>")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders a metric report correctly`() {
|
||||
val detektion = object : TestDetektion() {
|
||||
override val metrics: Collection<ProjectMetric> = listOf(
|
||||
ProjectMetric("M1", 10_000),
|
||||
ProjectMetric("M2", 2)
|
||||
)
|
||||
}
|
||||
val result = htmlReport.render(detektion)
|
||||
assertThat(result).contains("<li>10,000 M1</li>")
|
||||
assertThat(result).contains("<li>2 M2</li>")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders the complexity report correctly`() {
|
||||
val detektion = TestDetektion()
|
||||
detektion.addData(complexityKey, 10)
|
||||
detektion.addData(CognitiveComplexity.KEY, 10)
|
||||
detektion.addData(sourceLinesKey, 20)
|
||||
detektion.addData(logicalLinesKey, 10)
|
||||
detektion.addData(commentLinesKey, 2)
|
||||
detektion.addData(linesKey, 2222)
|
||||
val result = htmlReport.render(detektion)
|
||||
assertThat(result).contains("<li>2,222 lines of code (loc)</li>")
|
||||
assertThat(result).contains("<li>20 source lines of code (sloc)</li>")
|
||||
assertThat(result).contains("<li>10 logical lines of code (lloc)</li>")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders a blank complexity report correctly`() {
|
||||
val result = htmlReport.render(createTestDetektionWithMultipleSmells())
|
||||
assertThat(result).contains("<h2>Complexity Report</h2>\n\n<div>\n <ul></ul>\n</div>")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `asserts that the generated HTML is the same as expected`() {
|
||||
val expected = resourceAsPath("HtmlOutputFormatTest.html")
|
||||
var result = htmlReport.render(createTestDetektionWithMultipleSmells())
|
||||
result = generatedRegex.replace(result, replacement)
|
||||
|
||||
val actual = createTempFileForTest("actual-report", ".html")
|
||||
Files.write(actual, result.toByteArray())
|
||||
|
||||
assertThat(actual).hasSameTextualContentAs(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `asserts that the generated HTML is the same even if we change the order of the findings`() {
|
||||
val findings = findings()
|
||||
val reversedFindings = findings
|
||||
.reversedArray()
|
||||
.map { (section, findings) -> section to findings.asReversed() }
|
||||
.toTypedArray()
|
||||
|
||||
val firstReport = createReportWithFindings(findings)
|
||||
val secondReport = createReportWithFindings(reversedFindings)
|
||||
|
||||
assertThat(firstReport).hasSameTextualContentAs(secondReport)
|
||||
}
|
||||
assertThat(firstReport).hasSameTextualContentAs(secondReport)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,144 +4,140 @@ import io.gitlab.arturbosch.detekt.api.SourceLocation
|
||||
import kotlinx.html.div
|
||||
import kotlinx.html.stream.createHTML
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class HtmlUtilsSpec {
|
||||
|
||||
@Nested
|
||||
inner class `HTML snippet code` {
|
||||
private val code = """
|
||||
package cases
|
||||
// reports 1 - line with just one space
|
||||
private val code = """
|
||||
package cases
|
||||
// reports 1 - line with just one space
|
||||
|
||||
// reports 1 - a comment with trailing space
|
||||
// A comment
|
||||
// reports 1
|
||||
class TrailingWhitespacePositive {
|
||||
// reports 1 - line with just one tab
|
||||
|
||||
// reports 1 - a comment with trailing space
|
||||
// A comment
|
||||
// reports 1
|
||||
class TrailingWhitespacePositive {
|
||||
// reports 1 - line with just one tab
|
||||
|
||||
// reports 1
|
||||
fun myFunction() {
|
||||
// reports 1 - line with 1 trailing tab
|
||||
println("A message")
|
||||
// reports 1
|
||||
}
|
||||
fun myFunction() {
|
||||
// reports 1 - line with 1 trailing tab
|
||||
println("A message")
|
||||
// reports 1
|
||||
}
|
||||
""".trimIndent().splitToSequence('\n')
|
||||
}
|
||||
""".trimIndent().splitToSequence('\n')
|
||||
|
||||
@Test
|
||||
fun `all line`() {
|
||||
val snippet = createHTML().div() {
|
||||
snippetCode("ruleName", code.asSequence(), SourceLocation(7, 1), 34)
|
||||
}
|
||||
|
||||
assertThat(snippet).isEqualTo(
|
||||
"""
|
||||
<div>
|
||||
<pre><code><span class="lineno"> 4 </span>// reports 1 - a comment with trailing space
|
||||
<span class="lineno"> 5 </span>// A comment
|
||||
<span class="lineno"> 6 </span>// reports 1
|
||||
<span class="lineno"> 7 </span><span class="error">class TrailingWhitespacePositive {</span>
|
||||
<span class="lineno"> 8 </span> // reports 1 - line with just one tab
|
||||
<span class="lineno"> 9 </span>
|
||||
<span class="lineno"> 10 </span> // reports 1
|
||||
</code></pre>
|
||||
</div>
|
||||
|
||||
""".trimIndent()
|
||||
)
|
||||
@Test
|
||||
fun `all line`() {
|
||||
val snippet = createHTML().div() {
|
||||
snippetCode("ruleName", code.asSequence(), SourceLocation(7, 1), 34)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `part of line`() {
|
||||
val snippet = createHTML().div() {
|
||||
snippetCode("ruleName", code.asSequence(), SourceLocation(7, 7), 26)
|
||||
}
|
||||
assertThat(snippet).isEqualTo(
|
||||
"""
|
||||
<div>
|
||||
<pre><code><span class="lineno"> 4 </span>// reports 1 - a comment with trailing space
|
||||
<span class="lineno"> 5 </span>// A comment
|
||||
<span class="lineno"> 6 </span>// reports 1
|
||||
<span class="lineno"> 7 </span><span class="error">class TrailingWhitespacePositive {</span>
|
||||
<span class="lineno"> 8 </span> // reports 1 - line with just one tab
|
||||
<span class="lineno"> 9 </span>
|
||||
<span class="lineno"> 10 </span> // reports 1
|
||||
</code></pre>
|
||||
</div>
|
||||
|
||||
assertThat(snippet).isEqualTo(
|
||||
"""
|
||||
<div>
|
||||
<pre><code><span class="lineno"> 4 </span>// reports 1 - a comment with trailing space
|
||||
<span class="lineno"> 5 </span>// A comment
|
||||
<span class="lineno"> 6 </span>// reports 1
|
||||
<span class="lineno"> 7 </span>class <span class="error">TrailingWhitespacePositive</span> {
|
||||
<span class="lineno"> 8 </span> // reports 1 - line with just one tab
|
||||
<span class="lineno"> 9 </span>
|
||||
<span class="lineno"> 10 </span> // reports 1
|
||||
</code></pre>
|
||||
</div>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
""".trimIndent()
|
||||
)
|
||||
@Test
|
||||
fun `part of line`() {
|
||||
val snippet = createHTML().div() {
|
||||
snippetCode("ruleName", code.asSequence(), SourceLocation(7, 7), 26)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `more than one line`() {
|
||||
val snippet = createHTML().div() {
|
||||
snippetCode("ruleName", code.asSequence(), SourceLocation(7, 7), 66)
|
||||
}
|
||||
assertThat(snippet).isEqualTo(
|
||||
"""
|
||||
<div>
|
||||
<pre><code><span class="lineno"> 4 </span>// reports 1 - a comment with trailing space
|
||||
<span class="lineno"> 5 </span>// A comment
|
||||
<span class="lineno"> 6 </span>// reports 1
|
||||
<span class="lineno"> 7 </span>class <span class="error">TrailingWhitespacePositive</span> {
|
||||
<span class="lineno"> 8 </span> // reports 1 - line with just one tab
|
||||
<span class="lineno"> 9 </span>
|
||||
<span class="lineno"> 10 </span> // reports 1
|
||||
</code></pre>
|
||||
</div>
|
||||
|
||||
assertThat(snippet).isEqualTo(
|
||||
"""
|
||||
<div>
|
||||
<pre><code><span class="lineno"> 4 </span>// reports 1 - a comment with trailing space
|
||||
<span class="lineno"> 5 </span>// A comment
|
||||
<span class="lineno"> 6 </span>// reports 1
|
||||
<span class="lineno"> 7 </span>class <span class="error">TrailingWhitespacePositive {</span>
|
||||
<span class="lineno"> 8 </span><span class="error"> // reports 1 - line with just one</span> tab
|
||||
<span class="lineno"> 9 </span>
|
||||
<span class="lineno"> 10 </span> // reports 1
|
||||
</code></pre>
|
||||
</div>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
""".trimIndent()
|
||||
)
|
||||
@Test
|
||||
fun `more than one line`() {
|
||||
val snippet = createHTML().div() {
|
||||
snippetCode("ruleName", code.asSequence(), SourceLocation(7, 7), 66)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `first line`() {
|
||||
val snippet = createHTML().div() {
|
||||
snippetCode("ruleName", code.asSequence(), SourceLocation(1, 1), 1)
|
||||
}
|
||||
assertThat(snippet).isEqualTo(
|
||||
"""
|
||||
<div>
|
||||
<pre><code><span class="lineno"> 4 </span>// reports 1 - a comment with trailing space
|
||||
<span class="lineno"> 5 </span>// A comment
|
||||
<span class="lineno"> 6 </span>// reports 1
|
||||
<span class="lineno"> 7 </span>class <span class="error">TrailingWhitespacePositive {</span>
|
||||
<span class="lineno"> 8 </span><span class="error"> // reports 1 - line with just one</span> tab
|
||||
<span class="lineno"> 9 </span>
|
||||
<span class="lineno"> 10 </span> // reports 1
|
||||
</code></pre>
|
||||
</div>
|
||||
|
||||
assertThat(snippet).contains((1..4).map { " $it " })
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `first line`() {
|
||||
val snippet = createHTML().div() {
|
||||
snippetCode("ruleName", code.asSequence(), SourceLocation(1, 1), 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `second line`() {
|
||||
val snippet = createHTML().div() {
|
||||
snippetCode("ruleName", code.asSequence(), SourceLocation(2, 1), 1)
|
||||
}
|
||||
assertThat(snippet).contains((1..4).map { " $it " })
|
||||
}
|
||||
|
||||
assertThat(snippet).contains((1..5).map { " $it " })
|
||||
@Test
|
||||
fun `second line`() {
|
||||
val snippet = createHTML().div() {
|
||||
snippetCode("ruleName", code.asSequence(), SourceLocation(2, 1), 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `penultimate line`() {
|
||||
val snippet = createHTML().div() {
|
||||
snippetCode("ruleName", code.asSequence(), SourceLocation(15, 1), 1)
|
||||
}
|
||||
assertThat(snippet).contains((1..5).map { " $it " })
|
||||
}
|
||||
|
||||
assertThat(snippet).contains((12..16).map { " $it " })
|
||||
@Test
|
||||
fun `penultimate line`() {
|
||||
val snippet = createHTML().div() {
|
||||
snippetCode("ruleName", code.asSequence(), SourceLocation(15, 1), 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `last line`() {
|
||||
val snippet = createHTML().div() {
|
||||
snippetCode("ruleName", code.asSequence(), SourceLocation(16, 1), 1)
|
||||
}
|
||||
assertThat(snippet).contains((12..16).map { " $it " })
|
||||
}
|
||||
|
||||
assertThat(snippet).contains((13..16).map { " $it " })
|
||||
@Test
|
||||
fun `last line`() {
|
||||
val snippet = createHTML().div() {
|
||||
snippetCode("ruleName", code.asSequence(), SourceLocation(16, 1), 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when we provide an invalid source location the exception div is shown`() {
|
||||
val snippet = createHTML().div() {
|
||||
snippetCode("ruleName", code.asSequence(), SourceLocation(7, 100), 1)
|
||||
}
|
||||
assertThat(snippet).contains((13..16).map { " $it " })
|
||||
}
|
||||
|
||||
assertThat(snippet).contains("""<div class="exception">""")
|
||||
@Test
|
||||
fun `when we provide an invalid source location the exception div is shown`() {
|
||||
val snippet = createHTML().div() {
|
||||
snippetCode("ruleName", code.asSequence(), SourceLocation(7, 100), 1)
|
||||
}
|
||||
|
||||
assertThat(snippet).contains("""<div class="exception">""")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,63 +13,58 @@ import io.gitlab.arturbosch.detekt.test.createEntity
|
||||
import io.gitlab.arturbosch.detekt.test.createFindingForRelativePath
|
||||
import io.gitlab.arturbosch.detekt.test.createIssue
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.nio.file.Paths
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
class SarifOutputReportSpec {
|
||||
|
||||
@Nested
|
||||
inner class `sarif output report` {
|
||||
@Test
|
||||
fun `renders multiple issues`() {
|
||||
val result = TestDetektion(
|
||||
createFinding(ruleName = "TestSmellA", severity = SeverityLevel.ERROR),
|
||||
createFinding(ruleName = "TestSmellB", severity = SeverityLevel.WARNING),
|
||||
createFinding(ruleName = "TestSmellC", severity = SeverityLevel.INFO)
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `renders multiple issues`() {
|
||||
val result = TestDetektion(
|
||||
createFinding(ruleName = "TestSmellA", severity = SeverityLevel.ERROR),
|
||||
createFinding(ruleName = "TestSmellB", severity = SeverityLevel.WARNING),
|
||||
createFinding(ruleName = "TestSmellC", severity = SeverityLevel.INFO)
|
||||
)
|
||||
val report = SarifOutputReport()
|
||||
.apply { init(EmptySetupContext()) }
|
||||
.render(result)
|
||||
|
||||
val report = SarifOutputReport()
|
||||
.apply { init(EmptySetupContext()) }
|
||||
.render(result)
|
||||
assertThat(report).isEqualToIgnoringWhitespace(readResourceContent("vanilla.sarif.json"))
|
||||
}
|
||||
|
||||
assertThat(report).isEqualToIgnoringWhitespace(readResourceContent("vanilla.sarif.json"))
|
||||
}
|
||||
@Test
|
||||
fun `renders multiple issues with relative path`() {
|
||||
val basePath = "/Users/tester/detekt/"
|
||||
val result = TestDetektion(
|
||||
createFindingForRelativePath(ruleName = "TestSmellA", basePath = basePath),
|
||||
createFindingForRelativePath(ruleName = "TestSmellB", basePath = basePath),
|
||||
createFindingForRelativePath(ruleName = "TestSmellC", basePath = basePath)
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `renders multiple issues with relative path`() {
|
||||
val basePath = "/Users/tester/detekt/"
|
||||
val result = TestDetektion(
|
||||
createFindingForRelativePath(ruleName = "TestSmellA", basePath = basePath),
|
||||
createFindingForRelativePath(ruleName = "TestSmellB", basePath = basePath),
|
||||
createFindingForRelativePath(ruleName = "TestSmellC", basePath = basePath)
|
||||
)
|
||||
|
||||
val report = SarifOutputReport()
|
||||
.apply {
|
||||
init(
|
||||
EmptySetupContext().apply {
|
||||
register(DETEKT_OUTPUT_REPORT_BASE_PATH_KEY, Paths.get(basePath))
|
||||
}
|
||||
)
|
||||
}
|
||||
.render(result)
|
||||
.stripWhitespace()
|
||||
|
||||
val expectedReport = readResourceContent("relative_path.sarif.json")
|
||||
|
||||
// Note: Github CI uses D: drive, but it could be any drive for local development
|
||||
val systemAwareExpectedReport = if (whichOS().startsWith("windows", ignoreCase = true)) {
|
||||
val winRoot = Paths.get("/").toAbsolutePath().toString().replace("\\", "/")
|
||||
expectedReport.replace("file:///", "file://$winRoot")
|
||||
} else {
|
||||
expectedReport
|
||||
val report = SarifOutputReport()
|
||||
.apply {
|
||||
init(
|
||||
EmptySetupContext().apply {
|
||||
register(DETEKT_OUTPUT_REPORT_BASE_PATH_KEY, Paths.get(basePath))
|
||||
}
|
||||
)
|
||||
}
|
||||
.render(result)
|
||||
.stripWhitespace()
|
||||
|
||||
assertThat(report).isEqualToIgnoringWhitespace(systemAwareExpectedReport)
|
||||
val expectedReport = readResourceContent("relative_path.sarif.json")
|
||||
|
||||
// Note: Github CI uses D: drive, but it could be any drive for local development
|
||||
val systemAwareExpectedReport = if (whichOS().startsWith("windows", ignoreCase = true)) {
|
||||
val winRoot = Paths.get("/").toAbsolutePath().toString().replace("\\", "/")
|
||||
expectedReport.replace("file:///", "file://$winRoot")
|
||||
} else {
|
||||
expectedReport
|
||||
}
|
||||
|
||||
assertThat(report).isEqualToIgnoringWhitespace(systemAwareExpectedReport)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,45 +3,40 @@ package io.github.detekt.report.txt
|
||||
import io.gitlab.arturbosch.detekt.test.TestDetektion
|
||||
import io.gitlab.arturbosch.detekt.test.createFinding
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class TxtOutputReportSpec {
|
||||
|
||||
@Nested
|
||||
inner class `TXT output report` {
|
||||
@Test
|
||||
fun `renders none`() {
|
||||
val report = TxtOutputReport()
|
||||
val detektion = TestDetektion()
|
||||
val renderedText = ""
|
||||
assertThat(report.render(detektion)).isEqualTo(renderedText)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders none`() {
|
||||
val report = TxtOutputReport()
|
||||
val detektion = TestDetektion()
|
||||
val renderedText = ""
|
||||
assertThat(report.render(detektion)).isEqualTo(renderedText)
|
||||
}
|
||||
@Test
|
||||
fun `renders one`() {
|
||||
val report = TxtOutputReport()
|
||||
val detektion = TestDetektion(createFinding())
|
||||
val renderedText = "TestSmell - [TestEntity] at TestFile.kt:1:1 - Signature=TestEntitySignature\n"
|
||||
assertThat(report.render(detektion)).isEqualTo(renderedText)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders one`() {
|
||||
val report = TxtOutputReport()
|
||||
val detektion = TestDetektion(createFinding())
|
||||
val renderedText = "TestSmell - [TestEntity] at TestFile.kt:1:1 - Signature=TestEntitySignature\n"
|
||||
assertThat(report.render(detektion)).isEqualTo(renderedText)
|
||||
}
|
||||
@Test
|
||||
fun `renders multiple`() {
|
||||
val report = TxtOutputReport()
|
||||
val detektion = TestDetektion(
|
||||
createFinding(ruleName = "TestSmellA"),
|
||||
createFinding(ruleName = "TestSmellB"),
|
||||
createFinding(ruleName = "TestSmellC")
|
||||
)
|
||||
val renderedText = """
|
||||
TestSmellA - [TestEntity] at TestFile.kt:1:1 - Signature=TestEntitySignature
|
||||
TestSmellB - [TestEntity] at TestFile.kt:1:1 - Signature=TestEntitySignature
|
||||
TestSmellC - [TestEntity] at TestFile.kt:1:1 - Signature=TestEntitySignature
|
||||
|
||||
@Test
|
||||
fun `renders multiple`() {
|
||||
val report = TxtOutputReport()
|
||||
val detektion = TestDetektion(
|
||||
createFinding(ruleName = "TestSmellA"),
|
||||
createFinding(ruleName = "TestSmellB"),
|
||||
createFinding(ruleName = "TestSmellC")
|
||||
)
|
||||
val renderedText = """
|
||||
TestSmellA - [TestEntity] at TestFile.kt:1:1 - Signature=TestEntitySignature
|
||||
TestSmellB - [TestEntity] at TestFile.kt:1:1 - Signature=TestEntitySignature
|
||||
TestSmellC - [TestEntity] at TestFile.kt:1:1 - Signature=TestEntitySignature
|
||||
|
||||
""".trimIndent()
|
||||
assertThat(report.render(detektion)).isEqualTo(renderedText)
|
||||
}
|
||||
""".trimIndent()
|
||||
assertThat(report.render(detektion)).isEqualTo(renderedText)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ class XmlOutputFormatSpec {
|
||||
FilePath.fromAbsolute(Paths.get("src/main/com/sample/Sample1.kt"))
|
||||
)
|
||||
)
|
||||
val entity2 = Entity(
|
||||
private val entity2 = Entity(
|
||||
"Sample2",
|
||||
"",
|
||||
Location(
|
||||
@@ -46,174 +46,170 @@ class XmlOutputFormatSpec {
|
||||
)
|
||||
private val outputFormat = XmlOutputReport()
|
||||
|
||||
@Test
|
||||
fun `renders empty report`() {
|
||||
val result = outputFormat.render(TestDetektion())
|
||||
|
||||
assertThat(result).isEqualTo(
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<checkstyle version="4.3">
|
||||
</checkstyle>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders one reported issue in single file`() {
|
||||
val smell = CodeSmell(Issue("id_a", Severity.CodeSmell, "", Debt.TWENTY_MINS), entity1, message = "")
|
||||
|
||||
val result = outputFormat.render(TestDetektion(smell))
|
||||
|
||||
assertThat(result).isEqualTo(
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<checkstyle version="4.3">
|
||||
<file name="src/main/com/sample/Sample1.kt">
|
||||
$TAB<error line="11" column="1" severity="warning" message="" source="detekt.id_a" />
|
||||
</file>
|
||||
</checkstyle>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders two reported issues in single file`() {
|
||||
val smell1 = CodeSmell(Issue("id_a", Severity.CodeSmell, "", Debt.TWENTY_MINS), entity1, message = "")
|
||||
val smell2 = CodeSmell(Issue("id_b", Severity.CodeSmell, "", Debt.TWENTY_MINS), entity1, message = "")
|
||||
|
||||
val result = outputFormat.render(TestDetektion(smell1, smell2))
|
||||
|
||||
assertThat(result).isEqualTo(
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<checkstyle version="4.3">
|
||||
<file name="src/main/com/sample/Sample1.kt">
|
||||
$TAB<error line="11" column="1" severity="warning" message="" source="detekt.id_a" />
|
||||
$TAB<error line="11" column="1" severity="warning" message="" source="detekt.id_b" />
|
||||
</file>
|
||||
</checkstyle>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders one reported issue across multiple files`() {
|
||||
val smell1 = CodeSmell(Issue("id_a", Severity.CodeSmell, "", Debt.TWENTY_MINS), entity1, message = "")
|
||||
val smell2 = CodeSmell(Issue("id_a", Severity.CodeSmell, "", Debt.TWENTY_MINS), entity2, message = "")
|
||||
|
||||
val result = outputFormat.render(TestDetektion(smell1, smell2))
|
||||
|
||||
assertThat(result).isEqualTo(
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<checkstyle version="4.3">
|
||||
<file name="src/main/com/sample/Sample1.kt">
|
||||
$TAB<error line="11" column="1" severity="warning" message="" source="detekt.id_a" />
|
||||
</file>
|
||||
<file name="src/main/com/sample/Sample2.kt">
|
||||
$TAB<error line="22" column="2" severity="warning" message="" source="detekt.id_a" />
|
||||
</file>
|
||||
</checkstyle>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders issues with relative path`() {
|
||||
val findingA = createFindingForRelativePath(
|
||||
ruleName = "id_a",
|
||||
basePath = "/Users/tester/detekt/",
|
||||
relativePath = "Sample1.kt"
|
||||
)
|
||||
val findingB = createFindingForRelativePath(
|
||||
ruleName = "id_b",
|
||||
basePath = "/Users/tester/detekt/",
|
||||
relativePath = "Sample2.kt"
|
||||
)
|
||||
|
||||
val result = outputFormat.render(TestDetektion(findingA, findingB))
|
||||
|
||||
assertThat(result).isEqualTo(
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<checkstyle version="4.3">
|
||||
<file name="Sample1.kt">
|
||||
$TAB<error line="1" column="1" severity="warning" message="TestMessage" source="detekt.id_a" />
|
||||
</file>
|
||||
<file name="Sample2.kt">
|
||||
$TAB<error line="1" column="1" severity="warning" message="TestMessage" source="detekt.id_b" />
|
||||
</file>
|
||||
</checkstyle>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders two reported issues across multiple files`() {
|
||||
val smell1 = CodeSmell(Issue("id_a", Severity.CodeSmell, "", Debt.TWENTY_MINS), entity1, message = "")
|
||||
val smell2 = CodeSmell(Issue("id_b", Severity.CodeSmell, "", Debt.TWENTY_MINS), entity1, message = "")
|
||||
val smell3 = CodeSmell(Issue("id_a", Severity.CodeSmell, "", Debt.TWENTY_MINS), entity2, message = "")
|
||||
val smell4 = CodeSmell(Issue("id_b", Severity.CodeSmell, "", Debt.TWENTY_MINS), entity2, message = "")
|
||||
|
||||
val result = outputFormat.render(
|
||||
TestDetektion(
|
||||
smell1,
|
||||
smell2,
|
||||
smell3,
|
||||
smell4
|
||||
)
|
||||
)
|
||||
|
||||
assertThat(result).isEqualTo(
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<checkstyle version="4.3">
|
||||
<file name="src/main/com/sample/Sample1.kt">
|
||||
$TAB<error line="11" column="1" severity="warning" message="" source="detekt.id_a" />
|
||||
$TAB<error line="11" column="1" severity="warning" message="" source="detekt.id_b" />
|
||||
</file>
|
||||
<file name="src/main/com/sample/Sample2.kt">
|
||||
$TAB<error line="22" column="2" severity="warning" message="" source="detekt.id_a" />
|
||||
$TAB<error line="22" column="2" severity="warning" message="" source="detekt.id_b" />
|
||||
</file>
|
||||
</checkstyle>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `XML output format` {
|
||||
inner class `severity level conversion` {
|
||||
|
||||
@Test
|
||||
fun `renders empty report`() {
|
||||
val result = outputFormat.render(TestDetektion())
|
||||
|
||||
assertThat(result).isEqualTo(
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<checkstyle version="4.3">
|
||||
</checkstyle>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders one reported issue in single file`() {
|
||||
val smell = CodeSmell(Issue("id_a", Severity.CodeSmell, "", Debt.TWENTY_MINS), entity1, message = "")
|
||||
|
||||
val result = outputFormat.render(TestDetektion(smell))
|
||||
|
||||
assertThat(result).isEqualTo(
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<checkstyle version="4.3">
|
||||
<file name="src/main/com/sample/Sample1.kt">
|
||||
$TAB<error line="11" column="1" severity="warning" message="" source="detekt.id_a" />
|
||||
</file>
|
||||
</checkstyle>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders two reported issues in single file`() {
|
||||
val smell1 = CodeSmell(Issue("id_a", Severity.CodeSmell, "", Debt.TWENTY_MINS), entity1, message = "")
|
||||
val smell2 = CodeSmell(Issue("id_b", Severity.CodeSmell, "", Debt.TWENTY_MINS), entity1, message = "")
|
||||
|
||||
val result = outputFormat.render(TestDetektion(smell1, smell2))
|
||||
|
||||
assertThat(result).isEqualTo(
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<checkstyle version="4.3">
|
||||
<file name="src/main/com/sample/Sample1.kt">
|
||||
$TAB<error line="11" column="1" severity="warning" message="" source="detekt.id_a" />
|
||||
$TAB<error line="11" column="1" severity="warning" message="" source="detekt.id_b" />
|
||||
</file>
|
||||
</checkstyle>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders one reported issue across multiple files`() {
|
||||
val smell1 = CodeSmell(Issue("id_a", Severity.CodeSmell, "", Debt.TWENTY_MINS), entity1, message = "")
|
||||
val smell2 = CodeSmell(Issue("id_a", Severity.CodeSmell, "", Debt.TWENTY_MINS), entity2, message = "")
|
||||
|
||||
val result = outputFormat.render(TestDetektion(smell1, smell2))
|
||||
|
||||
assertThat(result).isEqualTo(
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<checkstyle version="4.3">
|
||||
<file name="src/main/com/sample/Sample1.kt">
|
||||
$TAB<error line="11" column="1" severity="warning" message="" source="detekt.id_a" />
|
||||
</file>
|
||||
<file name="src/main/com/sample/Sample2.kt">
|
||||
$TAB<error line="22" column="2" severity="warning" message="" source="detekt.id_a" />
|
||||
</file>
|
||||
</checkstyle>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders issues with relative path`() {
|
||||
val findingA = createFindingForRelativePath(
|
||||
ruleName = "id_a",
|
||||
basePath = "/Users/tester/detekt/",
|
||||
relativePath = "Sample1.kt"
|
||||
)
|
||||
val findingB = createFindingForRelativePath(
|
||||
ruleName = "id_b",
|
||||
basePath = "/Users/tester/detekt/",
|
||||
relativePath = "Sample2.kt"
|
||||
)
|
||||
|
||||
val result = outputFormat.render(TestDetektion(findingA, findingB))
|
||||
|
||||
assertThat(result).isEqualTo(
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<checkstyle version="4.3">
|
||||
<file name="Sample1.kt">
|
||||
$TAB<error line="1" column="1" severity="warning" message="TestMessage" source="detekt.id_a" />
|
||||
</file>
|
||||
<file name="Sample2.kt">
|
||||
$TAB<error line="1" column="1" severity="warning" message="TestMessage" source="detekt.id_b" />
|
||||
</file>
|
||||
</checkstyle>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders two reported issues across multiple files`() {
|
||||
val smell1 = CodeSmell(Issue("id_a", Severity.CodeSmell, "", Debt.TWENTY_MINS), entity1, message = "")
|
||||
val smell2 = CodeSmell(Issue("id_b", Severity.CodeSmell, "", Debt.TWENTY_MINS), entity1, message = "")
|
||||
val smell3 = CodeSmell(Issue("id_a", Severity.CodeSmell, "", Debt.TWENTY_MINS), entity2, message = "")
|
||||
val smell4 = CodeSmell(Issue("id_b", Severity.CodeSmell, "", Debt.TWENTY_MINS), entity2, message = "")
|
||||
|
||||
val result = outputFormat.render(
|
||||
TestDetektion(
|
||||
smell1,
|
||||
smell2,
|
||||
smell3,
|
||||
smell4
|
||||
)
|
||||
)
|
||||
|
||||
assertThat(result).isEqualTo(
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<checkstyle version="4.3">
|
||||
<file name="src/main/com/sample/Sample1.kt">
|
||||
$TAB<error line="11" column="1" severity="warning" message="" source="detekt.id_a" />
|
||||
$TAB<error line="11" column="1" severity="warning" message="" source="detekt.id_b" />
|
||||
</file>
|
||||
<file name="src/main/com/sample/Sample2.kt">
|
||||
$TAB<error line="22" column="2" severity="warning" message="" source="detekt.id_a" />
|
||||
$TAB<error line="22" column="2" severity="warning" message="" source="detekt.id_b" />
|
||||
</file>
|
||||
</checkstyle>
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `severity level conversion` {
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(SeverityLevel::class)
|
||||
fun `renders detektion with severity as XML with severity`(severity: SeverityLevel) {
|
||||
val xmlSeverity = severity.name.toLowerCase(Locale.US)
|
||||
val finding = object : CodeSmell(
|
||||
issue = Issue("issue_id", Severity.CodeSmell, "issue description", Debt.FIVE_MINS),
|
||||
entity = entity1,
|
||||
message = "message"
|
||||
) {
|
||||
override val severity: SeverityLevel
|
||||
get() = severity
|
||||
}
|
||||
|
||||
val expected = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<checkstyle version="4.3">
|
||||
<file name="src/main/com/sample/Sample1.kt">
|
||||
$TAB<error line="${finding.location.source.line}" column="${finding.location.source.column}" severity="$xmlSeverity" message="${finding.messageOrDescription()}" source="detekt.${finding.id}" />
|
||||
</file>
|
||||
</checkstyle>
|
||||
"""
|
||||
|
||||
val actual = outputFormat.render(TestDetektion(finding))
|
||||
|
||||
assertThat(actual).isEqualTo(expected.trimIndent())
|
||||
@ParameterizedTest
|
||||
@EnumSource(SeverityLevel::class)
|
||||
fun `renders detektion with severity as XML with severity`(severity: SeverityLevel) {
|
||||
val xmlSeverity = severity.name.toLowerCase(Locale.US)
|
||||
val finding = object : CodeSmell(
|
||||
issue = Issue("issue_id", Severity.CodeSmell, "issue description", Debt.FIVE_MINS),
|
||||
entity = entity1,
|
||||
message = "message"
|
||||
) {
|
||||
override val severity: SeverityLevel
|
||||
get() = severity
|
||||
}
|
||||
|
||||
val expected = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<checkstyle version="4.3">
|
||||
<file name="src/main/com/sample/Sample1.kt">
|
||||
$TAB<error line="${finding.location.source.line}" column="${finding.location.source.column}" severity="$xmlSeverity" message="${finding.messageOrDescription()}" source="detekt.${finding.id}" />
|
||||
</file>
|
||||
</checkstyle>
|
||||
"""
|
||||
|
||||
val actual = outputFormat.render(TestDetektion(finding))
|
||||
|
||||
assertThat(actual).isEqualTo(expected.trimIndent())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,26 +2,21 @@ package io.gitlab.arturbosch.detekt.rules.complexity
|
||||
|
||||
import io.gitlab.arturbosch.detekt.test.compileAndLint
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class ComplexConditionSpec {
|
||||
|
||||
@Nested
|
||||
inner class `ComplexCondition rule` {
|
||||
val code = """
|
||||
val a = if (5 > 4 && 4 < 6 || (3 < 5 || 2 < 5)) { 42 } else { 24 }
|
||||
|
||||
val code = """
|
||||
val a = if (5 > 4 && 4 < 6 || (3 < 5 || 2 < 5)) { 42 } else { 24 }
|
||||
|
||||
fun complexConditions() {
|
||||
while (5 > 4 && 4 < 6 || (3 < 5 || 2 < 5)) {}
|
||||
do { } while (5 > 4 && 4 < 6 || (3 < 5 || 2 < 5))
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `reports some complex conditions`() {
|
||||
assertThat(ComplexCondition().compileAndLint(code)).hasSize(3)
|
||||
fun complexConditions() {
|
||||
while (5 > 4 && 4 < 6 || (3 < 5 || 2 < 5)) {}
|
||||
do { } while (5 > 4 && 4 < 6 || (3 < 5 || 2 < 5))
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `reports some complex conditions`() {
|
||||
assertThat(ComplexCondition().compileAndLint(code)).hasSize(3)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,222 +17,218 @@ class ComplexMethodSpec {
|
||||
val defaultComplexity = 1
|
||||
|
||||
@Nested
|
||||
inner class `ComplexMethod rule` {
|
||||
inner class `different complex constructs` {
|
||||
|
||||
@Nested
|
||||
inner class `different complex constructs` {
|
||||
@Test
|
||||
fun `counts different loops`() {
|
||||
val findings = ComplexMethod(TestConfig(defaultConfigMap)).compileAndLint(
|
||||
"""
|
||||
fun test() {
|
||||
for (i in 1..10) {}
|
||||
while (true) {}
|
||||
do {} while(true)
|
||||
(1..10).forEach {}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `counts different loops`() {
|
||||
val findings = ComplexMethod(TestConfig(defaultConfigMap)).compileAndLint(
|
||||
"""
|
||||
fun test() {
|
||||
for (i in 1..10) {}
|
||||
while (true) {}
|
||||
do {} while(true)
|
||||
(1..10).forEach {}
|
||||
}
|
||||
"""
|
||||
)
|
||||
assertThat(findings.first()).isThresholded().withValue(defaultComplexity + 4)
|
||||
}
|
||||
|
||||
assertThat(findings.first()).isThresholded().withValue(defaultComplexity + 4)
|
||||
}
|
||||
@Test
|
||||
fun `counts catch blocks`() {
|
||||
val findings = ComplexMethod(TestConfig(defaultConfigMap)).compileAndLint(
|
||||
"""
|
||||
fun test() {
|
||||
try {} catch(e: IllegalArgumentException) {} catch(e: Exception) {} finally {}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `counts catch blocks`() {
|
||||
val findings = ComplexMethod(TestConfig(defaultConfigMap)).compileAndLint(
|
||||
"""
|
||||
fun test() {
|
||||
try {} catch(e: IllegalArgumentException) {} catch(e: Exception) {} finally {}
|
||||
}
|
||||
"""
|
||||
)
|
||||
assertThat(findings.first()).isThresholded().withValue(defaultComplexity + 2)
|
||||
}
|
||||
|
||||
assertThat(findings.first()).isThresholded().withValue(defaultComplexity + 2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `counts nested conditional statements`() {
|
||||
val findings = ComplexMethod(TestConfig(defaultConfigMap)).compileAndLint(
|
||||
"""
|
||||
fun test() {
|
||||
try {
|
||||
while (true) {
|
||||
if (true) {
|
||||
when ("string") {
|
||||
"" -> println()
|
||||
else -> println()
|
||||
}
|
||||
@Test
|
||||
fun `counts nested conditional statements`() {
|
||||
val findings = ComplexMethod(TestConfig(defaultConfigMap)).compileAndLint(
|
||||
"""
|
||||
fun test() {
|
||||
try {
|
||||
while (true) {
|
||||
if (true) {
|
||||
when ("string") {
|
||||
"" -> println()
|
||||
else -> println()
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// only catches count
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(findings.first()).isThresholded().withValue(defaultComplexity + 4)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `nesting functions` {
|
||||
|
||||
val code = """
|
||||
fun test() {
|
||||
for (i in 1..10) {}
|
||||
(1..10).forEach {}
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `counts three with nesting function 'forEach'`() {
|
||||
val config = TestConfig(defaultConfigMap.plus("ignoreNestingFunctions" to "false"))
|
||||
assertExpectedComplexityValue(code, config, expectedValue = 3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `can ignore nesting functions like 'forEach'`() {
|
||||
val config = TestConfig(defaultConfigMap.plus("ignoreNestingFunctions" to "true"))
|
||||
assertExpectedComplexityValue(code, config, expectedValue = 2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `skips all if if the nested functions is empty`() {
|
||||
val config = TestConfig(defaultConfigMap.plus("nestingFunctions" to ""))
|
||||
assertExpectedComplexityValue(code, config, expectedValue = 2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `skips 'forEach' as it is not specified`() {
|
||||
val config = TestConfig(defaultConfigMap.plus("nestingFunctions" to "let,apply,also"))
|
||||
assertExpectedComplexityValue(code, config, expectedValue = 2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `skips 'forEach' as it is not specified list`() {
|
||||
val config = TestConfig(defaultConfigMap.plus("nestingFunctions" to listOf("let", "apply", "also")))
|
||||
assertExpectedComplexityValue(code, config, expectedValue = 2)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `several complex methods` {
|
||||
|
||||
val path = resourceAsPath("ComplexMethods.kt")
|
||||
|
||||
@Test
|
||||
fun `does not report complex methods with a single when expression`() {
|
||||
val config = TestConfig(
|
||||
mapOf(
|
||||
"threshold" to "4",
|
||||
"ignoreSingleWhenExpression" to "true"
|
||||
)
|
||||
)
|
||||
val subject = ComplexMethod(config)
|
||||
|
||||
assertThat(subject.lint(path)).hasSourceLocations(SourceLocation(43, 5))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports all complex methods`() {
|
||||
val config = TestConfig(mapOf("threshold" to "4"))
|
||||
val subject = ComplexMethod(config)
|
||||
|
||||
assertThat(subject.lint(path)).hasSourceLocations(
|
||||
SourceLocation(6, 5),
|
||||
SourceLocation(15, 5),
|
||||
SourceLocation(25, 5),
|
||||
SourceLocation(35, 5),
|
||||
SourceLocation(43, 5)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not trip for a reasonable amount of simple when entries when ignoreSimpleWhenEntries is true`() {
|
||||
val config = TestConfig(mapOf("ignoreSimpleWhenEntries" to "true"))
|
||||
val subject = ComplexMethod(config)
|
||||
val code = """
|
||||
fun f() {
|
||||
val map = HashMap<Any, String>()
|
||||
for ((key, value) in map) {
|
||||
when (key) {
|
||||
is Int -> print("int")
|
||||
is String -> print("String")
|
||||
is Float -> print("Float")
|
||||
is Double -> print("Double")
|
||||
is Byte -> print("Byte")
|
||||
is Short -> print("Short")
|
||||
is Long -> print("Long")
|
||||
is Boolean -> print("Boolean")
|
||||
else -> throw IllegalArgumentException("Unexpected type value")
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
val findings = subject.compileAndLint(code)
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `function containing object literal with many overridden functions` {
|
||||
|
||||
val code = """
|
||||
fun f(): List<Any> {
|
||||
return object : List<Any> {
|
||||
override val size: Int get() = TODO("not implemented")
|
||||
|
||||
override fun contains(element: Any): Boolean {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun containsAll(elements: Collection<Any>): Boolean {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun get(index: Int): Any {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun indexOf(element: Any): Int {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<Any> {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun lastIndexOf(element: Any): Int {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun listIterator(): ListIterator<Any> {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun listIterator(index: Int): ListIterator<Any> {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun subList(fromIndex: Int, toIndex: Int): List<Any> {
|
||||
TODO("not implemented")
|
||||
} finally {
|
||||
// only catches count
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(findings.first()).isThresholded().withValue(defaultComplexity + 4)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `nesting functions` {
|
||||
|
||||
val code = """
|
||||
fun test() {
|
||||
for (i in 1..10) {}
|
||||
(1..10).forEach {}
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `counts three with nesting function 'forEach'`() {
|
||||
val config = TestConfig(defaultConfigMap.plus("ignoreNestingFunctions" to "false"))
|
||||
assertExpectedComplexityValue(code, config, expectedValue = 3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `can ignore nesting functions like 'forEach'`() {
|
||||
val config = TestConfig(defaultConfigMap.plus("ignoreNestingFunctions" to "true"))
|
||||
assertExpectedComplexityValue(code, config, expectedValue = 2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `skips all if if the nested functions is empty`() {
|
||||
val config = TestConfig(defaultConfigMap.plus("nestingFunctions" to ""))
|
||||
assertExpectedComplexityValue(code, config, expectedValue = 2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `skips 'forEach' as it is not specified`() {
|
||||
val config = TestConfig(defaultConfigMap.plus("nestingFunctions" to "let,apply,also"))
|
||||
assertExpectedComplexityValue(code, config, expectedValue = 2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `skips 'forEach' as it is not specified list`() {
|
||||
val config = TestConfig(defaultConfigMap.plus("nestingFunctions" to listOf("let", "apply", "also")))
|
||||
assertExpectedComplexityValue(code, config, expectedValue = 2)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `several complex methods` {
|
||||
|
||||
val path = resourceAsPath("ComplexMethods.kt")
|
||||
|
||||
@Test
|
||||
fun `does not report complex methods with a single when expression`() {
|
||||
val config = TestConfig(
|
||||
mapOf(
|
||||
"threshold" to "4",
|
||||
"ignoreSingleWhenExpression" to "true"
|
||||
)
|
||||
)
|
||||
val subject = ComplexMethod(config)
|
||||
|
||||
assertThat(subject.lint(path)).hasSourceLocations(SourceLocation(43, 5))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports all complex methods`() {
|
||||
val config = TestConfig(mapOf("threshold" to "4"))
|
||||
val subject = ComplexMethod(config)
|
||||
|
||||
assertThat(subject.lint(path)).hasSourceLocations(
|
||||
SourceLocation(6, 5),
|
||||
SourceLocation(15, 5),
|
||||
SourceLocation(25, 5),
|
||||
SourceLocation(35, 5),
|
||||
SourceLocation(43, 5)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not trip for a reasonable amount of simple when entries when ignoreSimpleWhenEntries is true`() {
|
||||
val config = TestConfig(mapOf("ignoreSimpleWhenEntries" to "true"))
|
||||
val subject = ComplexMethod(config)
|
||||
val code = """
|
||||
fun f() {
|
||||
val map = HashMap<Any, String>()
|
||||
for ((key, value) in map) {
|
||||
when (key) {
|
||||
is Int -> print("int")
|
||||
is String -> print("String")
|
||||
is Float -> print("Float")
|
||||
is Double -> print("Double")
|
||||
is Byte -> print("Byte")
|
||||
is Short -> print("Short")
|
||||
is Long -> print("Long")
|
||||
is Boolean -> print("Boolean")
|
||||
else -> throw IllegalArgumentException("Unexpected type value")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `should not count these overridden functions to base functions complexity`() {
|
||||
assertThat(ComplexMethod().compileAndLint(code)).isEmpty()
|
||||
val findings = subject.compileAndLint(code)
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `function containing object literal with many overridden functions` {
|
||||
|
||||
val code = """
|
||||
fun f(): List<Any> {
|
||||
return object : List<Any> {
|
||||
override val size: Int get() = TODO("not implemented")
|
||||
|
||||
override fun contains(element: Any): Boolean {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun containsAll(elements: Collection<Any>): Boolean {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun get(index: Int): Any {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun indexOf(element: Any): Int {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<Any> {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun lastIndexOf(element: Any): Int {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun listIterator(): ListIterator<Any> {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun listIterator(index: Int): ListIterator<Any> {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun subList(fromIndex: Int, toIndex: Int): List<Any> {
|
||||
TODO("not implemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `should not count these overridden functions to base functions complexity`() {
|
||||
assertThat(ComplexMethod().compileAndLint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,174 +3,169 @@ package io.gitlab.arturbosch.detekt.rules.complexity
|
||||
import io.gitlab.arturbosch.detekt.test.TestConfig
|
||||
import io.gitlab.arturbosch.detekt.test.compileAndLint
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class LabeledExpressionSpec {
|
||||
|
||||
private val subject = LabeledExpression()
|
||||
|
||||
@Nested
|
||||
inner class `LabeledExpression rule` {
|
||||
|
||||
@Test
|
||||
fun `reports break and continue labels`() {
|
||||
val code = """
|
||||
fun f() {
|
||||
loop@ for (i in 1..3) {
|
||||
for (j in 1..3) {
|
||||
if (j == 4) break@loop
|
||||
if (j == 5) continue@loop
|
||||
}
|
||||
@Test
|
||||
fun `reports break and continue labels`() {
|
||||
val code = """
|
||||
fun f() {
|
||||
loop@ for (i in 1..3) {
|
||||
for (j in 1..3) {
|
||||
if (j == 4) break@loop
|
||||
if (j == 5) continue@loop
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(3)
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports implicit return label`() {
|
||||
val code = """
|
||||
fun f(range: IntRange) {
|
||||
range.forEach {
|
||||
if (it == 5) return@forEach
|
||||
println(it)
|
||||
}
|
||||
@Test
|
||||
fun `reports implicit return label`() {
|
||||
val code = """
|
||||
fun f(range: IntRange) {
|
||||
range.forEach {
|
||||
if (it == 5) return@forEach
|
||||
println(it)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports explicit return label`() {
|
||||
val code = """
|
||||
fun f(range: IntRange) {
|
||||
range.forEach label@{
|
||||
if (it == 5) return@label
|
||||
println(it)
|
||||
}
|
||||
@Test
|
||||
fun `reports explicit return label`() {
|
||||
val code = """
|
||||
fun f(range: IntRange) {
|
||||
range.forEach label@{
|
||||
if (it == 5) return@label
|
||||
println(it)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(2)
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports labels referencing inner and outer class`() {
|
||||
val code = """
|
||||
class Outer {
|
||||
inner class Inner {
|
||||
fun f() {
|
||||
val i = this@Inner
|
||||
emptyList<Int>().forEach Outer@{
|
||||
// references forEach label and not outer class
|
||||
if (it == 5) return@Outer
|
||||
println(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report inner class referencing outer class`() {
|
||||
val code = """
|
||||
@Test
|
||||
fun `reports labels referencing inner and outer class`() {
|
||||
val code = """
|
||||
class Outer {
|
||||
inner class Inner {
|
||||
fun f() {
|
||||
print(this@Outer)
|
||||
val i = this@Inner
|
||||
emptyList<Int>().forEach Outer@{
|
||||
// references forEach label and not outer class
|
||||
if (it == 5) return@Outer
|
||||
println(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report inner class referencing outer class in extension function`() {
|
||||
val code = """
|
||||
class Outer {
|
||||
inner class Inner {
|
||||
@Test
|
||||
fun `does not report inner class referencing outer class`() {
|
||||
val code = """
|
||||
class Outer {
|
||||
inner class Inner {
|
||||
fun f() {
|
||||
print(this@Outer)
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report inner class referencing outer class in extension function`() {
|
||||
val code = """
|
||||
class Outer {
|
||||
inner class Inner {
|
||||
fun Int.f() {
|
||||
print(this@Inner)
|
||||
print(this@Outer)
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report nested class referencing outer class in extension function`() {
|
||||
val code = """
|
||||
class Outer {
|
||||
class Nested {
|
||||
fun Int.f() {
|
||||
print(this@Nested)
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report inner classes referencing outer class in extension function`() {
|
||||
val code = """
|
||||
class Outer {
|
||||
inner class Inner {
|
||||
inner class InnerInner {
|
||||
fun f() {
|
||||
print(this@Outer)
|
||||
print(this@Inner)
|
||||
}
|
||||
fun Int.f() {
|
||||
print(this@Inner)
|
||||
print(this@Outer)
|
||||
print(this@InnerInner)
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report nested class referencing outer class in extension function`() {
|
||||
val code = """
|
||||
class Outer {
|
||||
class Nested {
|
||||
fun Int.f() {
|
||||
print(this@Nested)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun `does not report excluded label`() {
|
||||
val code = """
|
||||
fun f() {
|
||||
loop@ for (i in 1..5) {}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
"""
|
||||
val config = TestConfig(mapOf("ignoredLabels" to listOf("loop")))
|
||||
val findings = LabeledExpression(config).compileAndLint(code)
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report inner classes referencing outer class in extension function`() {
|
||||
val code = """
|
||||
class Outer {
|
||||
inner class Inner {
|
||||
inner class InnerInner {
|
||||
fun f() {
|
||||
print(this@Outer)
|
||||
print(this@Inner)
|
||||
}
|
||||
fun Int.f() {
|
||||
print(this@Inner)
|
||||
print(this@InnerInner)
|
||||
}
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun `does not report excluded label config with string`() {
|
||||
val code = """
|
||||
fun f() {
|
||||
loop@ for (i in 1..5) {}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
"""
|
||||
val config = TestConfig(mapOf("ignoredLabels" to "loop"))
|
||||
val findings = LabeledExpression(config).compileAndLint(code)
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report excluded label`() {
|
||||
val code = """
|
||||
fun f() {
|
||||
loop@ for (i in 1..5) {}
|
||||
}
|
||||
"""
|
||||
val config = TestConfig(mapOf("ignoredLabels" to listOf("loop")))
|
||||
val findings = LabeledExpression(config).compileAndLint(code)
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report excluded label config with string`() {
|
||||
val code = """
|
||||
fun f() {
|
||||
loop@ for (i in 1..5) {}
|
||||
}
|
||||
"""
|
||||
val config = TestConfig(mapOf("ignoredLabels" to "loop"))
|
||||
val findings = LabeledExpression(config).compileAndLint(code)
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report excluded label config with leading and trailing wildcard`() {
|
||||
val code = """
|
||||
fun f() {
|
||||
loop@ for (i in 1..5) {}
|
||||
}
|
||||
"""
|
||||
val config = TestConfig(mapOf("ignoredLabels" to "*loop*,other"))
|
||||
val findings = LabeledExpression(config).compileAndLint(code)
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
@Test
|
||||
fun `does not report excluded label config with leading and trailing wildcard`() {
|
||||
val code = """
|
||||
fun f() {
|
||||
loop@ for (i in 1..5) {}
|
||||
}
|
||||
"""
|
||||
val config = TestConfig(mapOf("ignoredLabels" to "*loop*,other"))
|
||||
val findings = LabeledExpression(config).compileAndLint(code)
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,158 +4,153 @@ import io.gitlab.arturbosch.detekt.api.ThresholdedCodeSmell
|
||||
import io.gitlab.arturbosch.detekt.test.TestConfig
|
||||
import io.gitlab.arturbosch.detekt.test.assertThat
|
||||
import io.gitlab.arturbosch.detekt.test.compileAndLint
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class LongMethodSpec {
|
||||
|
||||
val subject = LongMethod(TestConfig(mapOf("threshold" to 5)))
|
||||
|
||||
@Nested
|
||||
inner class `nested functions can be long` {
|
||||
|
||||
@Test
|
||||
fun `should find two long methods`() {
|
||||
val code = """
|
||||
fun longMethod() { // 5 lines
|
||||
@Test
|
||||
fun `should find two long methods`() {
|
||||
val code = """
|
||||
fun longMethod() { // 5 lines
|
||||
println()
|
||||
println()
|
||||
println()
|
||||
|
||||
fun nestedLongMethod() { // 5 lines
|
||||
println()
|
||||
println()
|
||||
println()
|
||||
|
||||
fun nestedLongMethod() { // 5 lines
|
||||
println()
|
||||
println()
|
||||
println()
|
||||
}
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLint(code)
|
||||
|
||||
assertThat(findings).hasSize(2)
|
||||
assertThat(findings).hasTextLocations("longMethod", "nestedLongMethod")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not find too long methods`() {
|
||||
val code = """
|
||||
fun methodOk() { // 3 lines
|
||||
println()
|
||||
fun localMethodOk() { // 4 lines
|
||||
println()
|
||||
println()
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not find too long method with params on newlines`() {
|
||||
val code = """
|
||||
fun methodWithParams(
|
||||
param1: String
|
||||
) { // 4 lines
|
||||
println()
|
||||
println()
|
||||
}
|
||||
"""
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLint(code)
|
||||
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
assertThat(findings).hasSize(2)
|
||||
assertThat(findings).hasTextLocations("longMethod", "nestedLongMethod")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should find too long method with params on newlines`() {
|
||||
val code = """
|
||||
fun longMethodWithParams(
|
||||
@Test
|
||||
fun `should not find too long methods`() {
|
||||
val code = """
|
||||
fun methodOk() { // 3 lines
|
||||
println()
|
||||
fun localMethodOk() { // 4 lines
|
||||
println()
|
||||
println()
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not find too long method with params on newlines`() {
|
||||
val code = """
|
||||
fun methodWithParams(
|
||||
param1: String
|
||||
) { // 4 lines
|
||||
println()
|
||||
println()
|
||||
}
|
||||
"""
|
||||
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should find too long method with params on newlines`() {
|
||||
val code = """
|
||||
fun longMethodWithParams(
|
||||
param1: String
|
||||
) { // 5 lines
|
||||
println()
|
||||
println()
|
||||
println()
|
||||
}
|
||||
"""
|
||||
|
||||
val findings = subject.compileAndLint(code)
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings[0] as ThresholdedCodeSmell).hasValue(5)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should find long method with method call with params on separate lines`() {
|
||||
val code = """
|
||||
fun longMethod(
|
||||
x1: Int,
|
||||
x2: Int,
|
||||
y1: Int,
|
||||
y2: Int
|
||||
) { // 8 lines
|
||||
listOf(
|
||||
x1,
|
||||
y1,
|
||||
x2,
|
||||
y2
|
||||
)
|
||||
}
|
||||
"""
|
||||
|
||||
val findings = subject.compileAndLint(code)
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings[0] as ThresholdedCodeSmell).hasValue(8)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should find two long methods with params on separate lines`() {
|
||||
val code = """
|
||||
fun longMethod(
|
||||
param1: String
|
||||
) { // 5 lines
|
||||
println()
|
||||
println()
|
||||
println()
|
||||
|
||||
fun nestedLongMethod(
|
||||
param1: String
|
||||
) { // 5 lines
|
||||
println()
|
||||
println()
|
||||
println()
|
||||
}
|
||||
"""
|
||||
}
|
||||
"""
|
||||
|
||||
val findings = subject.compileAndLint(code)
|
||||
val findings = subject.compileAndLint(code)
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings[0] as ThresholdedCodeSmell).hasValue(5)
|
||||
}
|
||||
assertThat(findings).hasSize(2)
|
||||
assertThat(findings).hasTextLocations("longMethod", "nestedLongMethod")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should find long method with method call with params on separate lines`() {
|
||||
val code = """
|
||||
fun longMethod(
|
||||
x1: Int,
|
||||
x2: Int,
|
||||
y1: Int,
|
||||
y2: Int
|
||||
) { // 8 lines
|
||||
listOf(
|
||||
x1,
|
||||
y1,
|
||||
x2,
|
||||
y2
|
||||
)
|
||||
}
|
||||
"""
|
||||
|
||||
val findings = subject.compileAndLint(code)
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings[0] as ThresholdedCodeSmell).hasValue(8)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should find two long methods with params on separate lines`() {
|
||||
val code = """
|
||||
fun longMethod(
|
||||
@Test
|
||||
fun `should find nested long methods with params on separate lines`() {
|
||||
val code = """
|
||||
fun longMethod(
|
||||
param1: String
|
||||
) { // 4 lines
|
||||
println()
|
||||
println()
|
||||
|
||||
fun nestedLongMethod(
|
||||
param1: String
|
||||
) { // 5 lines
|
||||
println()
|
||||
println()
|
||||
println()
|
||||
|
||||
fun nestedLongMethod(
|
||||
param1: String
|
||||
) { // 5 lines
|
||||
println()
|
||||
println()
|
||||
println()
|
||||
}
|
||||
}
|
||||
"""
|
||||
}
|
||||
"""
|
||||
|
||||
val findings = subject.compileAndLint(code)
|
||||
val findings = subject.compileAndLint(code)
|
||||
|
||||
assertThat(findings).hasSize(2)
|
||||
assertThat(findings).hasTextLocations("longMethod", "nestedLongMethod")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should find nested long methods with params on separate lines`() {
|
||||
val code = """
|
||||
fun longMethod(
|
||||
param1: String
|
||||
) { // 4 lines
|
||||
println()
|
||||
println()
|
||||
|
||||
fun nestedLongMethod(
|
||||
param1: String
|
||||
) { // 5 lines
|
||||
println()
|
||||
println()
|
||||
println()
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
val findings = subject.compileAndLint(code)
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings).hasTextLocations("nestedLongMethod")
|
||||
assertThat(findings[0] as ThresholdedCodeSmell).hasValue(5)
|
||||
}
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings).hasTextLocations("nestedLongMethod")
|
||||
assertThat(findings[0] as ThresholdedCodeSmell).hasValue(5)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,145 +19,141 @@ class LongParameterListSpec {
|
||||
|
||||
val subject = LongParameterList(defaultConfig)
|
||||
|
||||
val reportMessageForFunction = "The function long(a: Int, b: Int) has too many parameters. " +
|
||||
"The current threshold is set to $defaultThreshold."
|
||||
val reportMessageForConstructor = "The constructor(a: Int, b: Int) has too many parameters. " +
|
||||
"The current threshold is set to $defaultThreshold."
|
||||
|
||||
@Test
|
||||
fun `reports too long parameter list`() {
|
||||
val code = "fun long(a: Int, b: Int) {}"
|
||||
val findings = subject.compileAndLint(code)
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings.first().message).isEqualTo(reportMessageForFunction)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report short parameter list`() {
|
||||
val code = "fun long(a: Int) {}"
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports too long parameter list event for parameters with defaults`() {
|
||||
val code = "fun long(a: Int, b: Int = 1) {}"
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report long parameter list if parameters with defaults should be ignored`() {
|
||||
val config = TestConfig(mapOf("ignoreDefaultParameters" to "true"))
|
||||
val rule = LongParameterList(config)
|
||||
val code = "fun long(a: Int, b: Int, c: Int = 2) {}"
|
||||
assertThat(rule.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports too long parameter list for primary constructors`() {
|
||||
val code = "class LongCtor(a: Int, b: Int)"
|
||||
val findings = subject.compileAndLint(code)
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings.first().message).isEqualTo(reportMessageForConstructor)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report short parameter list for primary constructors`() {
|
||||
val code = "class LongCtor(a: Int)"
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports too long parameter list for secondary constructors`() {
|
||||
val code = "class LongCtor() { constructor(a: Int, b: Int) : this() }"
|
||||
val findings = subject.compileAndLint(code)
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings.first().message).isEqualTo(reportMessageForConstructor)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report short parameter list for secondary constructors`() {
|
||||
val code = "class LongCtor() { constructor(a: Int) : this() }"
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports long parameter list if custom threshold is set`() {
|
||||
val config = TestConfig(mapOf("constructorThreshold" to "1"))
|
||||
val rule = LongParameterList(config)
|
||||
val code = "class LongCtor(a: Int)"
|
||||
assertThat(rule.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report long parameter list for constructors of data classes if asked`() {
|
||||
val config = TestConfig(
|
||||
mapOf(
|
||||
"ignoreDataClasses" to "true",
|
||||
"constructorThreshold" to "1"
|
||||
)
|
||||
)
|
||||
val rule = LongParameterList(config)
|
||||
val code = "data class Data(val a: Int)"
|
||||
assertThat(rule.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `LongParameterList rule` {
|
||||
inner class `constructors and functions with ignored annotations` {
|
||||
|
||||
val reportMessageForFunction = "The function long(a: Int, b: Int) has too many parameters. " +
|
||||
"The current threshold is set to $defaultThreshold."
|
||||
val reportMessageForConstructor = "The constructor(a: Int, b: Int) has too many parameters. " +
|
||||
"The current threshold is set to $defaultThreshold."
|
||||
val config =
|
||||
TestConfig(
|
||||
mapOf(
|
||||
"ignoreAnnotatedParameter" to listOf(
|
||||
"Generated",
|
||||
"kotlin.Deprecated",
|
||||
"kotlin.jvm.JvmName",
|
||||
"kotlin.Suppress"
|
||||
),
|
||||
"functionThreshold" to 1,
|
||||
"constructorThreshold" to 1
|
||||
)
|
||||
)
|
||||
|
||||
val rule = LongParameterList(config)
|
||||
|
||||
@Test
|
||||
fun `reports too long parameter list`() {
|
||||
val code = "fun long(a: Int, b: Int) {}"
|
||||
val findings = subject.compileAndLint(code)
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings.first().message).isEqualTo(reportMessageForFunction)
|
||||
}
|
||||
fun `reports long parameter list for constructors if constructor parameters are annotated with annotation that is not ignored`() {
|
||||
val code = """
|
||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||
annotation class CustomAnnotation
|
||||
|
||||
@Test
|
||||
fun `does not report short parameter list`() {
|
||||
val code = "fun long(a: Int) {}"
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports too long parameter list event for parameters with defaults`() {
|
||||
val code = "fun long(a: Int, b: Int = 1) {}"
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report long parameter list if parameters with defaults should be ignored`() {
|
||||
val config = TestConfig(mapOf("ignoreDefaultParameters" to "true"))
|
||||
val rule = LongParameterList(config)
|
||||
val code = "fun long(a: Int, b: Int, c: Int = 2) {}"
|
||||
assertThat(rule.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports too long parameter list for primary constructors`() {
|
||||
val code = "class LongCtor(a: Int, b: Int)"
|
||||
val findings = subject.compileAndLint(code)
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings.first().message).isEqualTo(reportMessageForConstructor)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report short parameter list for primary constructors`() {
|
||||
val code = "class LongCtor(a: Int)"
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports too long parameter list for secondary constructors`() {
|
||||
val code = "class LongCtor() { constructor(a: Int, b: Int) : this() }"
|
||||
val findings = subject.compileAndLint(code)
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings.first().message).isEqualTo(reportMessageForConstructor)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report short parameter list for secondary constructors`() {
|
||||
val code = "class LongCtor() { constructor(a: Int) : this() }"
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports long parameter list if custom threshold is set`() {
|
||||
val config = TestConfig(mapOf("constructorThreshold" to "1"))
|
||||
val rule = LongParameterList(config)
|
||||
val code = "class LongCtor(a: Int)"
|
||||
class Data constructor(@CustomAnnotation val a: Int)
|
||||
"""
|
||||
assertThat(rule.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report long parameter list for constructors of data classes if asked`() {
|
||||
val config = TestConfig(
|
||||
mapOf(
|
||||
"ignoreDataClasses" to "true",
|
||||
"constructorThreshold" to "1"
|
||||
)
|
||||
)
|
||||
val rule = LongParameterList(config)
|
||||
val code = "data class Data(val a: Int)"
|
||||
fun `reports long parameter list for functions if enough function parameters are annotated with annotation that is not ignored`() {
|
||||
val code = """
|
||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||
annotation class CustomAnnotation
|
||||
|
||||
class Data { fun foo(@CustomAnnotation a: Int) {} }
|
||||
"""
|
||||
assertThat(rule.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report long parameter list for constructors if enough constructor parameters are annotated with ignored annotation`() {
|
||||
val code = "class Data constructor(@kotlin.Suppress(\"\") val a: Int)"
|
||||
assertThat(rule.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `constructors and functions with ignored annotations` {
|
||||
|
||||
val config =
|
||||
TestConfig(
|
||||
mapOf(
|
||||
"ignoreAnnotatedParameter" to listOf(
|
||||
"Generated",
|
||||
"kotlin.Deprecated",
|
||||
"kotlin.jvm.JvmName",
|
||||
"kotlin.Suppress"
|
||||
),
|
||||
"functionThreshold" to 1,
|
||||
"constructorThreshold" to 1
|
||||
)
|
||||
)
|
||||
|
||||
val rule = LongParameterList(config)
|
||||
|
||||
@Test
|
||||
fun `reports long parameter list for constructors if constructor parameters are annotated with annotation that is not ignored`() {
|
||||
val code = """
|
||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||
annotation class CustomAnnotation
|
||||
|
||||
class Data constructor(@CustomAnnotation val a: Int)
|
||||
"""
|
||||
assertThat(rule.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports long parameter list for functions if enough function parameters are annotated with annotation that is not ignored`() {
|
||||
val code = """
|
||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||
annotation class CustomAnnotation
|
||||
|
||||
class Data { fun foo(@CustomAnnotation a: Int) {} }
|
||||
"""
|
||||
assertThat(rule.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report long parameter list for constructors if enough constructor parameters are annotated with ignored annotation`() {
|
||||
val code = "class Data constructor(@kotlin.Suppress(\"\") val a: Int)"
|
||||
assertThat(rule.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report long parameter list for functions if enough function parameters are annotated with ignored annotation`() {
|
||||
val code = """class Data {
|
||||
fun foo(@kotlin.Suppress("") a: Int) {} }
|
||||
"""
|
||||
assertThat(rule.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
@Test
|
||||
fun `does not report long parameter list for functions if enough function parameters are annotated with ignored annotation`() {
|
||||
val code = """class Data {
|
||||
fun foo(@kotlin.Suppress("") a: Int) {} }
|
||||
"""
|
||||
assertThat(rule.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,266 +13,262 @@ class MethodOverloadingSpec {
|
||||
val subject = MethodOverloading(defaultConfig)
|
||||
|
||||
@Nested
|
||||
inner class `MethodOverloading rule` {
|
||||
inner class `several overloaded methods` {
|
||||
|
||||
@Nested
|
||||
inner class `several overloaded methods` {
|
||||
|
||||
@Test
|
||||
fun `reports overloaded methods which exceed the threshold`() {
|
||||
val code = """
|
||||
class Test {
|
||||
fun x() {}
|
||||
fun x(i: Int) {}
|
||||
fun x(i: Int, j: Int) {}
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLint(code)
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings[0].message).isEqualTo("The method 'x' is overloaded 3 times.")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports overloaded top level methods which exceed the threshold`() {
|
||||
val code = """
|
||||
@Test
|
||||
fun `reports overloaded methods which exceed the threshold`() {
|
||||
val code = """
|
||||
class Test {
|
||||
fun x() {}
|
||||
fun x(i: Int) {}
|
||||
fun x(i: Int, j: Int) {}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report overloaded methods which do not exceed the threshold`() {
|
||||
subject.compileAndLint(
|
||||
"""
|
||||
class Test {
|
||||
fun x() { }
|
||||
fun x(i: Int) { }
|
||||
}
|
||||
"""
|
||||
)
|
||||
assertThat(subject.findings.size).isZero()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `several overloaded extensions methods` {
|
||||
|
||||
@Test
|
||||
fun `does not report extension methods with a different receiver`() {
|
||||
subject.compileAndLint(
|
||||
"""
|
||||
fun Boolean.foo() {}
|
||||
fun Int.foo() {}
|
||||
fun Long.foo() {}
|
||||
"""
|
||||
)
|
||||
assertThat(subject.findings.size).isZero()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports extension methods with the same receiver`() {
|
||||
subject.compileAndLint(
|
||||
"""
|
||||
fun Int.foo() {}
|
||||
fun Int.foo(i: Int) {}
|
||||
fun Int.foo(i: String) {}
|
||||
"""
|
||||
)
|
||||
assertThat(subject.findings.size).isEqualTo(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `several nested overloaded methods` {
|
||||
|
||||
@Test
|
||||
fun `reports nested overloaded methods which exceed the threshold`() {
|
||||
val code = """
|
||||
class Outer {
|
||||
internal class Inner {
|
||||
fun f() {}
|
||||
fun f(i: Int) {}
|
||||
fun f(i: Int, j: Int) {}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report nested overloaded methods which do not exceed the threshold`() {
|
||||
val code = """
|
||||
class Outer {
|
||||
|
||||
fun f() {}
|
||||
|
||||
internal class Inner {
|
||||
fun f(i: Int) {}
|
||||
fun f(i: Int, j: Int) {}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `several overloaded methods inside objects` {
|
||||
|
||||
@Test
|
||||
fun `reports overloaded methods inside an object which exceed the threshold`() {
|
||||
val code = """
|
||||
object Test {
|
||||
fun f() {}
|
||||
fun f(i: Int) {}
|
||||
fun f(i: Int, j: Int) {}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report overloaded methods inside an object which do not exceed the threshold`() {
|
||||
val code = """
|
||||
object Test {
|
||||
fun f() {}
|
||||
fun f(i: Int) {}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports overloaded methods inside a companion object which exceed the threshold`() {
|
||||
val code = """
|
||||
class Test {
|
||||
companion object {
|
||||
fun f() {}
|
||||
fun f(i: Int) {}
|
||||
fun f(i: Int, j: Int) {}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report overloaded methods in a companion object that do not exceed the threshold`() {
|
||||
val code = """
|
||||
class Test {
|
||||
companion object {
|
||||
fun f() {}
|
||||
fun f(i: Int) {}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report overloaded methods in classes or objects that do not exceed the threshold`() {
|
||||
val code = """
|
||||
class Test {
|
||||
|
||||
fun f() {}
|
||||
|
||||
companion object {
|
||||
fun f() {}
|
||||
fun f(i: Int) {}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports overloaded methods inside an anonymous object expression`() {
|
||||
val code = """
|
||||
class A {
|
||||
|
||||
fun f() {
|
||||
object : Runnable {
|
||||
override fun run() {}
|
||||
fun run(i: Int) {}
|
||||
fun run(i: Int, j: Int) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `several overloaded methods inside enum classes` {
|
||||
|
||||
@Test
|
||||
fun `does not report overridden methods inside enum entries`() {
|
||||
val code = """
|
||||
enum class Test {
|
||||
E1 {
|
||||
override fun f() {}
|
||||
},
|
||||
E2 {
|
||||
override fun f() {}
|
||||
},
|
||||
E3 {
|
||||
override fun f() {}
|
||||
};
|
||||
|
||||
abstract fun f()
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports overloaded methods in enum entry`() {
|
||||
val code = """
|
||||
enum class Test {
|
||||
E {
|
||||
fun f(i: Int) {}
|
||||
fun f(i: Int, j: Int) {}
|
||||
};
|
||||
fun f() {}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports overloaded methods in enum class`() {
|
||||
val code = """
|
||||
enum class Test {
|
||||
E;
|
||||
|
||||
fun f() {}
|
||||
fun f(i: Int) {}
|
||||
fun f(i: Int, j: Int) {}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLint(code)
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings[0].message).isEqualTo("The method 'x' is overloaded 3 times.")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report a class without a body`() {
|
||||
val code = "class A"
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report overloaded local functions`() {
|
||||
fun `reports overloaded top level methods which exceed the threshold`() {
|
||||
val code = """
|
||||
fun top() {
|
||||
fun x() {}
|
||||
fun x(i: Int) {}
|
||||
fun x(i: Int, j: Int) {}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report overloaded methods which do not exceed the threshold`() {
|
||||
subject.compileAndLint(
|
||||
"""
|
||||
class Test {
|
||||
fun x() { }
|
||||
fun x(i: Int) { }
|
||||
}
|
||||
"""
|
||||
)
|
||||
assertThat(subject.findings.size).isZero()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `several overloaded extensions methods` {
|
||||
|
||||
@Test
|
||||
fun `does not report extension methods with a different receiver`() {
|
||||
subject.compileAndLint(
|
||||
"""
|
||||
fun Boolean.foo() {}
|
||||
fun Int.foo() {}
|
||||
fun Long.foo() {}
|
||||
"""
|
||||
)
|
||||
assertThat(subject.findings.size).isZero()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports extension methods with the same receiver`() {
|
||||
subject.compileAndLint(
|
||||
"""
|
||||
fun Int.foo() {}
|
||||
fun Int.foo(i: Int) {}
|
||||
fun Int.foo(i: String) {}
|
||||
"""
|
||||
)
|
||||
assertThat(subject.findings.size).isEqualTo(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `several nested overloaded methods` {
|
||||
|
||||
@Test
|
||||
fun `reports nested overloaded methods which exceed the threshold`() {
|
||||
val code = """
|
||||
class Outer {
|
||||
internal class Inner {
|
||||
fun f() {}
|
||||
fun f(i: Int) {}
|
||||
fun f(i: Int, j: Int) {}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report nested overloaded methods which do not exceed the threshold`() {
|
||||
val code = """
|
||||
class Outer {
|
||||
|
||||
fun f() {}
|
||||
fun f(i: Int) {}
|
||||
fun f(i: Int, j: Int) {}
|
||||
|
||||
internal class Inner {
|
||||
fun f(i: Int) {}
|
||||
fun f(i: Int, j: Int) {}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `several overloaded methods inside objects` {
|
||||
|
||||
@Test
|
||||
fun `reports overloaded methods inside an object which exceed the threshold`() {
|
||||
val code = """
|
||||
object Test {
|
||||
fun f() {}
|
||||
fun f(i: Int) {}
|
||||
fun f(i: Int, j: Int) {}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report overloaded methods inside an object which do not exceed the threshold`() {
|
||||
val code = """
|
||||
object Test {
|
||||
fun f() {}
|
||||
fun f(i: Int) {}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports overloaded methods inside a companion object which exceed the threshold`() {
|
||||
val code = """
|
||||
class Test {
|
||||
companion object {
|
||||
fun f() {}
|
||||
fun f(i: Int) {}
|
||||
fun f(i: Int, j: Int) {}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report overloaded methods in a companion object that do not exceed the threshold`() {
|
||||
val code = """
|
||||
class Test {
|
||||
companion object {
|
||||
fun f() {}
|
||||
fun f(i: Int) {}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report overloaded methods in classes or objects that do not exceed the threshold`() {
|
||||
val code = """
|
||||
class Test {
|
||||
|
||||
fun f() {}
|
||||
|
||||
companion object {
|
||||
fun f() {}
|
||||
fun f(i: Int) {}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports overloaded methods inside an anonymous object expression`() {
|
||||
val code = """
|
||||
class A {
|
||||
|
||||
fun f() {
|
||||
object : Runnable {
|
||||
override fun run() {}
|
||||
fun run(i: Int) {}
|
||||
fun run(i: Int, j: Int) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `several overloaded methods inside enum classes` {
|
||||
|
||||
@Test
|
||||
fun `does not report overridden methods inside enum entries`() {
|
||||
val code = """
|
||||
enum class Test {
|
||||
E1 {
|
||||
override fun f() {}
|
||||
},
|
||||
E2 {
|
||||
override fun f() {}
|
||||
},
|
||||
E3 {
|
||||
override fun f() {}
|
||||
};
|
||||
|
||||
abstract fun f()
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports overloaded methods in enum entry`() {
|
||||
val code = """
|
||||
enum class Test {
|
||||
E {
|
||||
fun f(i: Int) {}
|
||||
fun f(i: Int, j: Int) {}
|
||||
};
|
||||
fun f() {}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports overloaded methods in enum class`() {
|
||||
val code = """
|
||||
enum class Test {
|
||||
E;
|
||||
|
||||
fun f() {}
|
||||
fun f(i: Int) {}
|
||||
fun f(i: Int, j: Int) {}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report a class without a body`() {
|
||||
val code = "class A"
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report overloaded local functions`() {
|
||||
val code = """
|
||||
fun top() {
|
||||
fun f() {}
|
||||
fun f(i: Int) {}
|
||||
fun f(i: Int, j: Int) {}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,187 +14,244 @@ class NamedArgumentsSpec(val env: KotlinCoreEnvironment) {
|
||||
val defaultConfig = TestConfig(mapOf("threshold" to defaultThreshold))
|
||||
val subject = NamedArguments(defaultConfig)
|
||||
|
||||
@Nested
|
||||
inner class `NameArguments rule` {
|
||||
|
||||
@Test
|
||||
fun `invocation with more than 2 parameters should throw error`() {
|
||||
val code = """
|
||||
fun sum(a: Int, b:Int, c:Int) {
|
||||
println(a + b + c)
|
||||
}
|
||||
fun call() {
|
||||
sum(1, 2, 3)
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Function invocation with more than 2 parameters should not throw error if named`() {
|
||||
val code = """
|
||||
fun sum(a: Int, b:Int, c:Int) {
|
||||
println(a + b + c)
|
||||
}
|
||||
fun call() {
|
||||
sum(a = 1, b = 2, c = 3)
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invocation with more than 2 parameters should throw error if even one is not named`() {
|
||||
val code = """
|
||||
fun sum(a: Int, b:Int, c:Int) {
|
||||
println(a + b + c)
|
||||
}
|
||||
fun call() {
|
||||
sum(1, b = 2, c = 3)
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invocation with less than 3 parameters should not throw error`() {
|
||||
val code = """
|
||||
fun sum(a: Int, b:Int) {
|
||||
println(a + b)
|
||||
}
|
||||
fun call() {
|
||||
sum(1, 2)
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invocation with less than 3 named parameters should not throw error`() {
|
||||
val code = """
|
||||
fun sum(a: Int, b:Int) {
|
||||
println(a + b)
|
||||
}
|
||||
fun call() {
|
||||
sum(a = 1, b = 2)
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `constructor invocation with more than 3 non-named parameters should throw error`() {
|
||||
val code = """
|
||||
class C(val a: Int, val b:Int, val c:Int)
|
||||
|
||||
val obj = C(1, 2, 3)
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `constructor invocation with more than 3 named parameters should not throw error`() {
|
||||
val code = """
|
||||
class C(val a: Int, val b:Int, val c:Int)
|
||||
|
||||
val obj = C(a = 1, b = 2, c= 3)
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `constructor invocation with less than 3 non-named parameters should not throw error`() {
|
||||
val code = """
|
||||
class C(val a: Int, val b:Int)
|
||||
|
||||
val obj = C(1, 2)
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `java method invocation should not be flagged`() {
|
||||
val code = """
|
||||
import java.time.LocalDateTime
|
||||
|
||||
fun test() {
|
||||
LocalDateTime.of(2020, 3, 13, 14, 0, 0)
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invocation with varargs should not be flagged`() {
|
||||
val code = """
|
||||
fun foo(vararg i: Int) {}
|
||||
fun bar(a: Int, b: Int, c: Int, vararg s: String) {}
|
||||
fun test() {
|
||||
foo(1, 2, 3, 4, 5)
|
||||
bar(1, 2, 3, "a")
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invocation with spread operator should be flagged`() {
|
||||
val code = """
|
||||
fun bar(a: Int, b: Int, c: Int, vararg s: String) {}
|
||||
fun test() {
|
||||
bar(1, 2, 3, *arrayOf("a"))
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `lambda argument` {
|
||||
@Test
|
||||
fun `inner lambda argument`() {
|
||||
val code = """
|
||||
fun foo(a: Int, b: Int, c: Int, block: ((Int) -> Int)) {}
|
||||
|
||||
fun test() {
|
||||
foo(a = 1, b = 2, c = 3, { it })
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(1)
|
||||
@Test
|
||||
fun `invocation with more than 2 parameters should throw error`() {
|
||||
val code = """
|
||||
fun sum(a: Int, b:Int, c:Int) {
|
||||
println(a + b + c)
|
||||
}
|
||||
fun call() {
|
||||
sum(1, 2, 3)
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Function invocation with more than 2 parameters should not throw error if named`() {
|
||||
val code = """
|
||||
fun sum(a: Int, b:Int, c:Int) {
|
||||
println(a + b + c)
|
||||
}
|
||||
fun call() {
|
||||
sum(a = 1, b = 2, c = 3)
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invocation with more than 2 parameters should throw error if even one is not named`() {
|
||||
val code = """
|
||||
fun sum(a: Int, b:Int, c:Int) {
|
||||
println(a + b + c)
|
||||
}
|
||||
fun call() {
|
||||
sum(1, b = 2, c = 3)
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invocation with less than 3 parameters should not throw error`() {
|
||||
val code = """
|
||||
fun sum(a: Int, b:Int) {
|
||||
println(a + b)
|
||||
}
|
||||
fun call() {
|
||||
sum(1, 2)
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invocation with less than 3 named parameters should not throw error`() {
|
||||
val code = """
|
||||
fun sum(a: Int, b:Int) {
|
||||
println(a + b)
|
||||
}
|
||||
fun call() {
|
||||
sum(a = 1, b = 2)
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `constructor invocation with more than 3 non-named parameters should throw error`() {
|
||||
val code = """
|
||||
class C(val a: Int, val b:Int, val c:Int)
|
||||
|
||||
val obj = C(1, 2, 3)
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `constructor invocation with more than 3 named parameters should not throw error`() {
|
||||
val code = """
|
||||
class C(val a: Int, val b:Int, val c:Int)
|
||||
|
||||
val obj = C(a = 1, b = 2, c= 3)
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `constructor invocation with less than 3 non-named parameters should not throw error`() {
|
||||
val code = """
|
||||
class C(val a: Int, val b:Int)
|
||||
|
||||
val obj = C(1, 2)
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `java method invocation should not be flagged`() {
|
||||
val code = """
|
||||
import java.time.LocalDateTime
|
||||
|
||||
fun test() {
|
||||
LocalDateTime.of(2020, 3, 13, 14, 0, 0)
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invocation with varargs should not be flagged`() {
|
||||
val code = """
|
||||
fun foo(vararg i: Int) {}
|
||||
fun bar(a: Int, b: Int, c: Int, vararg s: String) {}
|
||||
fun test() {
|
||||
foo(1, 2, 3, 4, 5)
|
||||
bar(1, 2, 3, "a")
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invocation with spread operator should be flagged`() {
|
||||
val code = """
|
||||
fun bar(a: Int, b: Int, c: Int, vararg s: String) {}
|
||||
fun test() {
|
||||
bar(1, 2, 3, *arrayOf("a"))
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `lambda argument` {
|
||||
@Test
|
||||
fun `inner lambda argument`() {
|
||||
val code = """
|
||||
fun foo(a: Int, b: Int, c: Int, block: ((Int) -> Int)) {}
|
||||
|
||||
fun test() {
|
||||
foo(a = 1, b = 2, c = 3, { it })
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `outer lambda argument`() {
|
||||
val code = """
|
||||
fun foo(a: Int, b: Int, c: Int, block: ((Int) -> Int)) {}
|
||||
|
||||
fun test() {
|
||||
foo(a = 1, b = 2, c = 3) { it }
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unnamed argument and outer argument`() {
|
||||
val code = """
|
||||
fun foo(a: Int, b: Int, c: Int, block: ((Int) -> Int)) {}
|
||||
|
||||
fun test() {
|
||||
foo(a = 1, b = 2, 3) { it }
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class IgnoreArgumentsMatchingNames {
|
||||
@Nested
|
||||
inner class `ignoreArgumentsMatchingNames is true` {
|
||||
val subject =
|
||||
NamedArguments(TestConfig(mapOf("threshold" to 2, "ignoreArgumentsMatchingNames" to true)))
|
||||
|
||||
@Test
|
||||
fun `outer lambda argument`() {
|
||||
fun `all arguments are the same as the parameter names`() {
|
||||
val code = """
|
||||
fun foo(a: Int, b: Int, c: Int, block: ((Int) -> Int)) {}
|
||||
|
||||
fun test() {
|
||||
foo(a = 1, b = 2, c = 3) { it }
|
||||
}
|
||||
fun foo(a: Int, b: Int, c: Int) {}
|
||||
fun bar(a: Int, b: Int, c: Int) {
|
||||
foo(a, b, c)
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unnamed argument and outer argument`() {
|
||||
fun `some arguments are not the same as the parameter names`() {
|
||||
val code = """
|
||||
fun foo(a: Int, b: Int, c: Int, block: ((Int) -> Int)) {}
|
||||
|
||||
fun test() {
|
||||
foo(a = 1, b = 2, 3) { it }
|
||||
}
|
||||
fun foo(a: Int, b: Int, c: Int) {}
|
||||
fun bar(a: Int, b: Int, c: Int) {
|
||||
foo(a, c, b)
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `all arguments are the same as the parameter names and have a this receiver`() {
|
||||
val code = """
|
||||
class Baz {
|
||||
private var b: Int = 42
|
||||
fun foo(a: Int, b: Int, c: Int) {}
|
||||
fun bar(a: Int, c: Int) {
|
||||
foo(a, this.b, c)
|
||||
}
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `all arguments are the same as the parameter names and have a it receiver`() {
|
||||
val code = """
|
||||
data class Baz(val b: Int)
|
||||
fun foo(a: Int, b: Int, c: Int) {}
|
||||
fun bar(a: Int, c: Int, baz: Baz?) {
|
||||
baz?.let { foo(a, it.b, c) }
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(1)
|
||||
@@ -202,78 +259,17 @@ class NamedArgumentsSpec(val env: KotlinCoreEnvironment) {
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class IgnoreArgumentsMatchingNames {
|
||||
@Nested
|
||||
inner class `ignoreArgumentsMatchingNames is true` {
|
||||
val subject =
|
||||
NamedArguments(TestConfig(mapOf("threshold" to 2, "ignoreArgumentsMatchingNames" to true)))
|
||||
|
||||
@Test
|
||||
fun `all arguments are the same as the parameter names`() {
|
||||
val code = """
|
||||
fun foo(a: Int, b: Int, c: Int) {}
|
||||
fun bar(a: Int, b: Int, c: Int) {
|
||||
foo(a, b, c)
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `some arguments are not the same as the parameter names`() {
|
||||
val code = """
|
||||
fun foo(a: Int, b: Int, c: Int) {}
|
||||
fun bar(a: Int, b: Int, c: Int) {
|
||||
foo(a, c, b)
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `all arguments are the same as the parameter names and have a this receiver`() {
|
||||
val code = """
|
||||
class Baz {
|
||||
private var b: Int = 42
|
||||
fun foo(a: Int, b: Int, c: Int) {}
|
||||
fun bar(a: Int, c: Int) {
|
||||
foo(a, this.b, c)
|
||||
}
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `all arguments are the same as the parameter names and have a it receiver`() {
|
||||
val code = """
|
||||
data class Baz(val b: Int)
|
||||
fun foo(a: Int, b: Int, c: Int) {}
|
||||
fun bar(a: Int, c: Int, baz: Baz?) {
|
||||
baz?.let { foo(a, it.b, c) }
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `ignoreArgumentsMatchingNames is false` {
|
||||
@Test
|
||||
fun `all arguments are the same as parameter names`() {
|
||||
val code = """
|
||||
fun foo(a: Int, b: Int, c: Int) {}
|
||||
fun bar(a: Int, b: Int, c: Int) {
|
||||
foo(a, b, c)
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
inner class `ignoreArgumentsMatchingNames is false` {
|
||||
@Test
|
||||
fun `all arguments are the same as parameter names`() {
|
||||
val code = """
|
||||
fun foo(a: Int, b: Int, c: Int) {}
|
||||
fun bar(a: Int, b: Int, c: Int) {
|
||||
foo(a, b, c)
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLintWithContext(env, code)
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import io.gitlab.arturbosch.detekt.test.assertThat
|
||||
import io.gitlab.arturbosch.detekt.test.compileAndLint
|
||||
import io.gitlab.arturbosch.detekt.test.lint
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class NestedBlockDepthSpec {
|
||||
@@ -16,39 +15,18 @@ class NestedBlockDepthSpec {
|
||||
val defaultConfig = TestConfig(mapOf("threshold" to defaultThreshold))
|
||||
val subject = NestedBlockDepth(defaultConfig)
|
||||
|
||||
@Nested
|
||||
inner class `nested classes are also considered` {
|
||||
@Test
|
||||
fun `should detect only the nested large class`() {
|
||||
subject.lint(resourceAsPath("NestedClasses.kt"))
|
||||
assertThat(subject.findings).hasSize(1)
|
||||
assertThat((subject.findings[0] as ThresholdedCodeSmell).value).isEqualTo(5)
|
||||
}
|
||||
@Test
|
||||
fun `should detect only the nested large class`() {
|
||||
subject.lint(resourceAsPath("NestedClasses.kt"))
|
||||
assertThat(subject.findings).hasSize(1)
|
||||
assertThat((subject.findings[0] as ThresholdedCodeSmell).value).isEqualTo(5)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should detect too nested block depth`() {
|
||||
val code = """
|
||||
fun f() {
|
||||
if (true) {
|
||||
if (true) {
|
||||
if (true) {
|
||||
if (true) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLint(code)
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings).hasTextLocations(4 to 5)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not detect valid nested block depth`() {
|
||||
val code = """
|
||||
fun f() {
|
||||
@Test
|
||||
fun `should detect too nested block depth`() {
|
||||
val code = """
|
||||
fun f() {
|
||||
if (true) {
|
||||
if (true) {
|
||||
if (true) {
|
||||
if (true) {
|
||||
@@ -56,42 +34,60 @@ class NestedBlockDepthSpec {
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
"""
|
||||
val findings = subject.compileAndLint(code)
|
||||
|
||||
@Test
|
||||
fun `does not report valid nested if else branches`() {
|
||||
val code = """
|
||||
fun f() {
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings).hasTextLocations(4 to 5)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not detect valid nested block depth`() {
|
||||
val code = """
|
||||
fun f() {
|
||||
if (true) {
|
||||
if (true) {
|
||||
if (true) {
|
||||
} else if (true) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report valid nested if else branches`() {
|
||||
val code = """
|
||||
fun f() {
|
||||
if (true) {
|
||||
if (true) {
|
||||
} else if (true) {
|
||||
if (true) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports deeply nested if else branches`() {
|
||||
val code = """
|
||||
fun f() {
|
||||
if (true) {
|
||||
if (true) {
|
||||
} else if (true) {
|
||||
if (true) {
|
||||
if (true) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports deeply nested if else branches`() {
|
||||
val code = """
|
||||
fun f() {
|
||||
if (true) {
|
||||
if (true) {
|
||||
} else if (true) {
|
||||
if (true) {
|
||||
if (true) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import io.gitlab.arturbosch.detekt.rules.KotlinCoreEnvironmentTest
|
||||
import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@KotlinCoreEnvironmentTest
|
||||
@@ -12,67 +11,63 @@ class ReplaceSafeCallChainWithRunSpec(val env: KotlinCoreEnvironment) {
|
||||
|
||||
val subject = ReplaceSafeCallChainWithRun()
|
||||
|
||||
@Nested
|
||||
inner class `ReplaceSafeChainWithRun rule` {
|
||||
@Test
|
||||
fun `reports long chain of unnecessary safe qualified expressions`() {
|
||||
val code = """
|
||||
val x: String? = "string"
|
||||
|
||||
@Test
|
||||
fun `reports long chain of unnecessary safe qualified expressions`() {
|
||||
val code = """
|
||||
val x: String? = "string"
|
||||
val y = x
|
||||
?.asSequence()
|
||||
?.map { it }
|
||||
?.distinctBy { it }
|
||||
?.iterator()
|
||||
?.forEach(::println)
|
||||
"""
|
||||
|
||||
val y = x
|
||||
?.asSequence()
|
||||
?.map { it }
|
||||
?.distinctBy { it }
|
||||
?.iterator()
|
||||
?.forEach(::println)
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1)
|
||||
}
|
||||
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1)
|
||||
}
|
||||
@Test
|
||||
fun `reports short chain of unnecessary safe qualified expressions`() {
|
||||
val code = """
|
||||
val x: String? = "string"
|
||||
|
||||
@Test
|
||||
fun `reports short chain of unnecessary safe qualified expressions`() {
|
||||
val code = """
|
||||
val x: String? = "string"
|
||||
val y = x
|
||||
?.asSequence()
|
||||
?.map { it }
|
||||
"""
|
||||
|
||||
val y = x
|
||||
?.asSequence()
|
||||
?.map { it }
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1)
|
||||
}
|
||||
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1)
|
||||
}
|
||||
@Test
|
||||
fun `does not report a safe call chain which is too short to benefit`() {
|
||||
val code = """
|
||||
val x: String? = "string"
|
||||
|
||||
@Test
|
||||
fun `does not report a safe call chain which is too short to benefit`() {
|
||||
val code = """
|
||||
val x: String? = "string"
|
||||
val y = x
|
||||
?.asSequence()
|
||||
"""
|
||||
|
||||
val y = x
|
||||
?.asSequence()
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
@Test
|
||||
fun `does not report a safe call chain on left side of assignment`() {
|
||||
val code = """
|
||||
class Something {
|
||||
var element: Element? = null
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report a safe call chain on left side of assignment`() {
|
||||
val code = """
|
||||
class Something {
|
||||
var element: Element? = null
|
||||
}
|
||||
class Element(var list: List<String>?)
|
||||
|
||||
class Element(var list: List<String>?)
|
||||
val z: Something? = Something()
|
||||
|
||||
val z: Something? = Something()
|
||||
fun modifyList() {
|
||||
z?.element?.list = listOf("strings")
|
||||
}
|
||||
"""
|
||||
|
||||
fun modifyList() {
|
||||
z?.element?.list = listOf("strings")
|
||||
}
|
||||
"""
|
||||
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,145 +17,141 @@ class StringLiteralDuplicationSpec {
|
||||
val subject = StringLiteralDuplication()
|
||||
|
||||
@Nested
|
||||
inner class `StringLiteralDuplication rule` {
|
||||
|
||||
@Nested
|
||||
inner class `many hardcoded strings` {
|
||||
|
||||
@Test
|
||||
fun `reports 3 equal hardcoded strings`() {
|
||||
val code = """
|
||||
class Duplication {
|
||||
var s1 = "lorem"
|
||||
fun f(s: String = "lorem") {
|
||||
s1.equals("lorem")
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report 2 equal hardcoded strings`() {
|
||||
val code = """val str = "lorem" + "lorem" + "ipsum""""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `strings in annotations` {
|
||||
inner class `many hardcoded strings` {
|
||||
|
||||
@Test
|
||||
fun `reports 3 equal hardcoded strings`() {
|
||||
val code = """
|
||||
@Suppress("unused")
|
||||
class A
|
||||
@Suppress("unused")
|
||||
class B
|
||||
@Suppress("unused")
|
||||
class C
|
||||
class Duplication {
|
||||
var s1 = "lorem"
|
||||
fun f(s: String = "lorem") {
|
||||
s1.equals("lorem")
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `does not report strings in annotations`() {
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports strings in annotations according to config`() {
|
||||
val config = TestConfig(mapOf(IGNORE_ANNOTATION to "false"))
|
||||
assertFindingWithConfig(code, config, 1)
|
||||
}
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `strings with less than 5 characters` {
|
||||
@Test
|
||||
fun `does not report 2 equal hardcoded strings`() {
|
||||
val code = """val str = "lorem" + "lorem" + "ipsum""""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
val code = """val str = "amet" + "amet" + "amet""""
|
||||
@Nested
|
||||
inner class `strings in annotations` {
|
||||
|
||||
@Test
|
||||
fun `does not report strings with 4 characters`() {
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
val code = """
|
||||
@Suppress("unused")
|
||||
class A
|
||||
@Suppress("unused")
|
||||
class B
|
||||
@Suppress("unused")
|
||||
class C
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `reports string with 4 characters`() {
|
||||
val config = TestConfig(mapOf(EXCLUDE_SHORT_STRING to "false"))
|
||||
assertFindingWithConfig(code, config, 1)
|
||||
}
|
||||
@Test
|
||||
fun `does not report strings in annotations`() {
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `strings with values to match for the regex` {
|
||||
@Test
|
||||
fun `reports strings in annotations according to config`() {
|
||||
val config = TestConfig(mapOf(IGNORE_ANNOTATION to "false"))
|
||||
assertFindingWithConfig(code, config, 1)
|
||||
}
|
||||
}
|
||||
|
||||
val regexTestingCode = """
|
||||
@Nested
|
||||
inner class `strings with less than 5 characters` {
|
||||
|
||||
val code = """val str = "amet" + "amet" + "amet""""
|
||||
|
||||
@Test
|
||||
fun `does not report strings with 4 characters`() {
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports string with 4 characters`() {
|
||||
val config = TestConfig(mapOf(EXCLUDE_SHORT_STRING to "false"))
|
||||
assertFindingWithConfig(code, config, 1)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `strings with values to match for the regex` {
|
||||
|
||||
val regexTestingCode = """
|
||||
val str1 = "lorem" + "lorem" + "lorem"
|
||||
val str2 = "ipsum" + "ipsum" + "ipsum"
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `does not report lorem or ipsum according to config in regex`() {
|
||||
val code = """
|
||||
val str1 = "lorem" + "lorem" + "lorem"
|
||||
val str2 = "ipsum" + "ipsum" + "ipsum"
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `does not report lorem or ipsum according to config in regex`() {
|
||||
val code = """
|
||||
val str1 = "lorem" + "lorem" + "lorem"
|
||||
val str2 = "ipsum" + "ipsum" + "ipsum"
|
||||
"""
|
||||
val config = TestConfig(mapOf(IGNORE_STRINGS_REGEX to "(lorem|ipsum)"))
|
||||
assertFindingWithConfig(code, config, 0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not fail with invalid regex when disabled`() {
|
||||
val configValues = mapOf(
|
||||
"active" to "false",
|
||||
IGNORE_STRINGS_REGEX to "*lorem"
|
||||
)
|
||||
val config = TestConfig(configValues)
|
||||
assertFindingWithConfig(regexTestingCode, config, 0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should fail with invalid regex`() {
|
||||
val config = TestConfig(mapOf(IGNORE_STRINGS_REGEX to "*lorem"))
|
||||
assertThatExceptionOfType(PatternSyntaxException::class.java).isThrownBy {
|
||||
StringLiteralDuplication(config).compileAndLint(regexTestingCode)
|
||||
}
|
||||
}
|
||||
val config = TestConfig(mapOf(IGNORE_STRINGS_REGEX to "(lorem|ipsum)"))
|
||||
assertFindingWithConfig(code, config, 0)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `saves string literal references` {
|
||||
|
||||
@Test
|
||||
fun `reports 3 locations for 'lorem'`() {
|
||||
val code = """
|
||||
class Duplication {
|
||||
var s1 = "lorem"
|
||||
fun f(s: String = "lorem") {
|
||||
s1.equals("lorem")
|
||||
}
|
||||
}
|
||||
"""
|
||||
val finding = subject.compileAndLint(code)[0]
|
||||
val locations = finding.references.map { it.location } + finding.entity.location
|
||||
assertThat(locations).hasSize(3)
|
||||
}
|
||||
@Test
|
||||
fun `should not fail with invalid regex when disabled`() {
|
||||
val configValues = mapOf(
|
||||
"active" to "false",
|
||||
IGNORE_STRINGS_REGEX to "*lorem"
|
||||
)
|
||||
val config = TestConfig(configValues)
|
||||
assertFindingWithConfig(regexTestingCode, config, 0)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `multiline strings with string interpolation` {
|
||||
|
||||
@Test
|
||||
fun `does not report duplicated parts in multiline strings`() {
|
||||
val code = """
|
||||
// does not report because it treats the multiline string parts as one string
|
||||
val str = ""${'"'}
|
||||
|
|
||||
|
|
||||
|
|
||||
""${'"'}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
@Test
|
||||
fun `should fail with invalid regex`() {
|
||||
val config = TestConfig(mapOf(IGNORE_STRINGS_REGEX to "*lorem"))
|
||||
assertThatExceptionOfType(PatternSyntaxException::class.java).isThrownBy {
|
||||
StringLiteralDuplication(config).compileAndLint(regexTestingCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `saves string literal references` {
|
||||
|
||||
@Test
|
||||
fun `reports 3 locations for 'lorem'`() {
|
||||
val code = """
|
||||
class Duplication {
|
||||
var s1 = "lorem"
|
||||
fun f(s: String = "lorem") {
|
||||
s1.equals("lorem")
|
||||
}
|
||||
}
|
||||
"""
|
||||
val finding = subject.compileAndLint(code)[0]
|
||||
val locations = finding.references.map { it.location } + finding.entity.location
|
||||
assertThat(locations).hasSize(3)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `multiline strings with string interpolation` {
|
||||
|
||||
@Test
|
||||
fun `does not report duplicated parts in multiline strings`() {
|
||||
val code = """
|
||||
// does not report because it treats the multiline string parts as one string
|
||||
val str = ""${'"'}
|
||||
|
|
||||
|
|
||||
|
|
||||
""${'"'}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun assertFindingWithConfig(code: String, config: TestConfig, expected: Int) {
|
||||
|
||||
@@ -16,255 +16,251 @@ private const val IGNORE_PRIVATE = "ignorePrivate"
|
||||
private const val IGNORE_OVERRIDDEN = "ignoreOverridden"
|
||||
|
||||
class TooManyFunctionsSpec {
|
||||
@Nested
|
||||
inner class `different declarations with one function as threshold` {
|
||||
|
||||
val rule =
|
||||
TooManyFunctions(
|
||||
TestConfig(
|
||||
mapOf(
|
||||
THRESHOLD_IN_CLASSES to "1",
|
||||
THRESHOLD_IN_ENUMS to "1",
|
||||
THRESHOLD_IN_FILES to "1",
|
||||
THRESHOLD_IN_INTERFACES to "1",
|
||||
THRESHOLD_IN_OBJECTS to "1"
|
||||
)
|
||||
val rule =
|
||||
TooManyFunctions(
|
||||
TestConfig(
|
||||
mapOf(
|
||||
THRESHOLD_IN_CLASSES to "1",
|
||||
THRESHOLD_IN_ENUMS to "1",
|
||||
THRESHOLD_IN_FILES to "1",
|
||||
THRESHOLD_IN_INTERFACES to "1",
|
||||
THRESHOLD_IN_OBJECTS to "1"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `finds one function in class`() {
|
||||
val code = """
|
||||
class A {
|
||||
@Test
|
||||
fun `finds one function in class`() {
|
||||
val code = """
|
||||
class A {
|
||||
fun a() = Unit
|
||||
}
|
||||
"""
|
||||
|
||||
val findings = rule.compileAndLint(code)
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings).hasTextLocations(6 to 7)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `finds one function in object`() {
|
||||
val code = """
|
||||
object O {
|
||||
fun o() = Unit
|
||||
}
|
||||
"""
|
||||
|
||||
val findings = rule.compileAndLint(code)
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings).hasTextLocations(7 to 8)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `finds one function in interface`() {
|
||||
val code = """
|
||||
interface I {
|
||||
fun i()
|
||||
}
|
||||
"""
|
||||
|
||||
val findings = rule.compileAndLint(code)
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings).hasTextLocations(10 to 11)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `finds one function in enum`() {
|
||||
val code = """
|
||||
enum class E {
|
||||
A;
|
||||
fun e() {}
|
||||
}
|
||||
"""
|
||||
|
||||
val findings = rule.compileAndLint(code)
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings).hasTextLocations(11 to 12)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `finds one function in file`() {
|
||||
val code = "fun f() = Unit"
|
||||
|
||||
assertThat(rule.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `finds one function in file ignoring other declarations`() {
|
||||
val code = """
|
||||
fun f1() = Unit
|
||||
class C
|
||||
object O
|
||||
fun f2() = Unit
|
||||
interface I
|
||||
enum class E
|
||||
fun f3() = Unit
|
||||
"""
|
||||
|
||||
assertThat(rule.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `finds one function in nested class`() {
|
||||
val code = """
|
||||
class A {
|
||||
class B {
|
||||
fun a() = Unit
|
||||
}
|
||||
"""
|
||||
}
|
||||
"""
|
||||
|
||||
val findings = rule.compileAndLint(code)
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings).hasTextLocations(6 to 7)
|
||||
}
|
||||
val findings = rule.compileAndLint(code)
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings).hasTextLocations(20 to 21)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `finds one function in object`() {
|
||||
val code = """
|
||||
object O {
|
||||
fun o() = Unit
|
||||
}
|
||||
"""
|
||||
@Nested
|
||||
inner class `different deprecated functions` {
|
||||
val code = """
|
||||
@Deprecated("")
|
||||
fun f() {
|
||||
}
|
||||
|
||||
val findings = rule.compileAndLint(code)
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings).hasTextLocations(7 to 8)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `finds one function in interface`() {
|
||||
val code = """
|
||||
interface I {
|
||||
fun i()
|
||||
}
|
||||
"""
|
||||
|
||||
val findings = rule.compileAndLint(code)
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings).hasTextLocations(10 to 11)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `finds one function in enum`() {
|
||||
val code = """
|
||||
enum class E {
|
||||
A;
|
||||
fun e() {}
|
||||
}
|
||||
"""
|
||||
|
||||
val findings = rule.compileAndLint(code)
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings).hasTextLocations(11 to 12)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `finds one function in file`() {
|
||||
val code = "fun f() = Unit"
|
||||
|
||||
assertThat(rule.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `finds one function in file ignoring other declarations`() {
|
||||
val code = """
|
||||
fun f1() = Unit
|
||||
class C
|
||||
object O
|
||||
fun f2() = Unit
|
||||
interface I
|
||||
enum class E
|
||||
fun f3() = Unit
|
||||
"""
|
||||
|
||||
assertThat(rule.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `finds one function in nested class`() {
|
||||
val code = """
|
||||
class A {
|
||||
class B {
|
||||
fun a() = Unit
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
val findings = rule.compileAndLint(code)
|
||||
assertThat(findings).hasSize(1)
|
||||
assertThat(findings).hasTextLocations(20 to 21)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `different deprecated functions` {
|
||||
val code = """
|
||||
class A {
|
||||
@Deprecated("")
|
||||
fun f() {
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `finds all deprecated functions per default`() {
|
||||
assertThat(rule.compileAndLint(code)).hasSize(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `finds no deprecated functions`() {
|
||||
val configuredRule = TooManyFunctions(
|
||||
TestConfig(
|
||||
mapOf(
|
||||
THRESHOLD_IN_CLASSES to "1",
|
||||
THRESHOLD_IN_FILES to "1",
|
||||
IGNORE_DEPRECATED to "true"
|
||||
)
|
||||
)
|
||||
)
|
||||
assertThat(configuredRule.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `different private functions` {
|
||||
|
||||
val code = """
|
||||
class A {
|
||||
private fun f() {}
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `finds the private function per default`() {
|
||||
assertThat(rule.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `finds no private functions`() {
|
||||
val configuredRule = TooManyFunctions(
|
||||
TestConfig(
|
||||
mapOf(
|
||||
THRESHOLD_IN_CLASSES to "1",
|
||||
THRESHOLD_IN_FILES to "1",
|
||||
IGNORE_PRIVATE to "true"
|
||||
)
|
||||
)
|
||||
)
|
||||
assertThat(configuredRule.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `false negative when private and deprecated functions are ignored - #1439` {
|
||||
|
||||
@Test
|
||||
fun `should not report when file has no public functions`() {
|
||||
val code = """
|
||||
class A {
|
||||
private fun a() = Unit
|
||||
private fun b() = Unit
|
||||
@Deprecated("")
|
||||
fun f() {
|
||||
}
|
||||
private fun c() = Unit
|
||||
}
|
||||
|
||||
interface I {
|
||||
fun a() = Unit
|
||||
fun b() = Unit
|
||||
}
|
||||
|
||||
class B : I {
|
||||
override fun a() = Unit
|
||||
override fun b() = Unit
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `finds all deprecated functions per default`() {
|
||||
assertThat(rule.compileAndLint(code)).hasSize(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `finds no deprecated functions`() {
|
||||
val configuredRule = TooManyFunctions(
|
||||
TestConfig(
|
||||
mapOf(
|
||||
THRESHOLD_IN_CLASSES to "1",
|
||||
THRESHOLD_IN_FILES to "1",
|
||||
IGNORE_DEPRECATED to "true"
|
||||
)
|
||||
val configuredRule = TooManyFunctions(
|
||||
TestConfig(
|
||||
mapOf(
|
||||
THRESHOLD_IN_CLASSES to "1",
|
||||
THRESHOLD_IN_FILES to "1",
|
||||
IGNORE_PRIVATE to "true",
|
||||
IGNORE_DEPRECATED to "true",
|
||||
IGNORE_OVERRIDDEN to "true"
|
||||
)
|
||||
)
|
||||
assertThat(configuredRule.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
)
|
||||
assertThat(configuredRule.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `different private functions` {
|
||||
@Nested
|
||||
inner class `overridden functions` {
|
||||
|
||||
val code = """
|
||||
class A {
|
||||
private fun f() {}
|
||||
val code = """
|
||||
interface I1 {
|
||||
fun func1()
|
||||
fun func2()
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `finds the private function per default`() {
|
||||
assertThat(rule.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
class Foo : I1 {
|
||||
override fun func1() = Unit
|
||||
override fun func2() = Unit
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `finds no private functions`() {
|
||||
val configuredRule = TooManyFunctions(
|
||||
TestConfig(
|
||||
mapOf(
|
||||
THRESHOLD_IN_CLASSES to "1",
|
||||
THRESHOLD_IN_FILES to "1",
|
||||
IGNORE_PRIVATE to "true"
|
||||
)
|
||||
@Test
|
||||
fun `should not report class with overridden functions, if ignoreOverridden is enabled`() {
|
||||
val configuredRule = TooManyFunctions(
|
||||
TestConfig(
|
||||
mapOf(
|
||||
THRESHOLD_IN_CLASSES to "1",
|
||||
THRESHOLD_IN_FILES to "1",
|
||||
IGNORE_OVERRIDDEN to "true"
|
||||
)
|
||||
)
|
||||
assertThat(configuredRule.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
)
|
||||
assertThat(configuredRule.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `false negative when private and deprecated functions are ignored - #1439` {
|
||||
|
||||
@Test
|
||||
fun `should not report when file has no public functions`() {
|
||||
val code = """
|
||||
class A {
|
||||
private fun a() = Unit
|
||||
private fun b() = Unit
|
||||
@Deprecated("")
|
||||
private fun c() = Unit
|
||||
}
|
||||
|
||||
interface I {
|
||||
fun a() = Unit
|
||||
fun b() = Unit
|
||||
}
|
||||
|
||||
class B : I {
|
||||
override fun a() = Unit
|
||||
override fun b() = Unit
|
||||
}
|
||||
"""
|
||||
val configuredRule = TooManyFunctions(
|
||||
TestConfig(
|
||||
mapOf(
|
||||
THRESHOLD_IN_CLASSES to "1",
|
||||
THRESHOLD_IN_FILES to "1",
|
||||
IGNORE_PRIVATE to "true",
|
||||
IGNORE_DEPRECATED to "true",
|
||||
IGNORE_OVERRIDDEN to "true"
|
||||
)
|
||||
@Test
|
||||
fun `should count overridden functions, if ignoreOverridden is disabled`() {
|
||||
val configuredRule = TooManyFunctions(
|
||||
TestConfig(
|
||||
mapOf(
|
||||
THRESHOLD_IN_CLASSES to "1",
|
||||
THRESHOLD_IN_FILES to "1",
|
||||
IGNORE_OVERRIDDEN to "false"
|
||||
)
|
||||
)
|
||||
assertThat(configuredRule.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `overridden functions` {
|
||||
|
||||
val code = """
|
||||
interface I1 {
|
||||
fun func1()
|
||||
fun func2()
|
||||
}
|
||||
|
||||
class Foo : I1 {
|
||||
override fun func1() = Unit
|
||||
override fun func2() = Unit
|
||||
}
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `should not report class with overridden functions, if ignoreOverridden is enabled`() {
|
||||
val configuredRule = TooManyFunctions(
|
||||
TestConfig(
|
||||
mapOf(
|
||||
THRESHOLD_IN_CLASSES to "1",
|
||||
THRESHOLD_IN_FILES to "1",
|
||||
IGNORE_OVERRIDDEN to "true"
|
||||
)
|
||||
)
|
||||
)
|
||||
assertThat(configuredRule.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should count overridden functions, if ignoreOverridden is disabled`() {
|
||||
val configuredRule = TooManyFunctions(
|
||||
TestConfig(
|
||||
mapOf(
|
||||
THRESHOLD_IN_CLASSES to "1",
|
||||
THRESHOLD_IN_FILES to "1",
|
||||
IGNORE_OVERRIDDEN to "false"
|
||||
)
|
||||
)
|
||||
)
|
||||
assertThat(configuredRule.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
)
|
||||
assertThat(configuredRule.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,74 +4,69 @@ import io.gitlab.arturbosch.detekt.api.Config
|
||||
import io.gitlab.arturbosch.detekt.test.assertThat
|
||||
import io.gitlab.arturbosch.detekt.test.compileAndLint
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class GlobalCoroutineUsageSpec {
|
||||
val subject = GlobalCoroutineUsage(Config.empty)
|
||||
|
||||
@Nested
|
||||
inner class `GlobalCoroutineUsage rule` {
|
||||
@Test
|
||||
@DisplayName("should report GlobalScope.launch")
|
||||
fun reportGlobalScopeLaunch() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Test
|
||||
@DisplayName("should report GlobalScope.launch")
|
||||
fun reportGlobalScopeLaunch() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
fun foo() {
|
||||
GlobalScope.launch { delay(1_000L) }
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
fun foo() {
|
||||
GlobalScope.launch { delay(1_000L) }
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
@Test
|
||||
@DisplayName("should report GlobalScope.async")
|
||||
fun reportGlobalScopeAsync() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
|
||||
@Test
|
||||
@DisplayName("should report GlobalScope.async")
|
||||
fun reportGlobalScopeAsync() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
fun foo() {
|
||||
GlobalScope.async { delay(1_000L) }
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
fun foo() {
|
||||
GlobalScope.async { delay(1_000L) }
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
@Test
|
||||
fun `should not report bar(GlobalScope)`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
|
||||
@Test
|
||||
fun `should not report bar(GlobalScope)`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
fun bar(scope: CoroutineScope) = Unit
|
||||
|
||||
fun bar(scope: CoroutineScope) = Unit
|
||||
fun foo() {
|
||||
bar(GlobalScope)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
fun foo() {
|
||||
bar(GlobalScope)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
@Test
|
||||
fun `should not report 'val scope = GlobalScope'`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
|
||||
@Test
|
||||
fun `should not report 'val scope = GlobalScope'`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
fun bar(scope: CoroutineScope) = Unit
|
||||
|
||||
fun bar(scope: CoroutineScope) = Unit
|
||||
|
||||
fun foo() {
|
||||
val scope = GlobalScope
|
||||
bar(scope)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
fun foo() {
|
||||
val scope = GlobalScope
|
||||
bar(scope)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import io.gitlab.arturbosch.detekt.rules.KotlinCoreEnvironmentTest
|
||||
import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@KotlinCoreEnvironmentTest
|
||||
@@ -13,120 +12,116 @@ class RedundantSuspendModifierSpec(val env: KotlinCoreEnvironment) {
|
||||
|
||||
val subject = RedundantSuspendModifier(Config.empty)
|
||||
|
||||
@Nested
|
||||
inner class `RedundantSuspendModifier` {
|
||||
@Test
|
||||
fun `reports when public function returns expression of platform type`() {
|
||||
val code = """
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
@Test
|
||||
fun `reports when public function returns expression of platform type`() {
|
||||
val code = """
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
suspend fun suspendCoroutine() = suspendCoroutine { continuation: Continuation<String> ->
|
||||
continuation.resume("string")
|
||||
}
|
||||
|
||||
suspend fun suspendCoroutine() = suspendCoroutine { continuation: Continuation<String> ->
|
||||
continuation.resume("string")
|
||||
class RedundantSuspend {
|
||||
suspend fun redundantSuspend() {
|
||||
println("hello world")
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1)
|
||||
}
|
||||
|
||||
class RedundantSuspend {
|
||||
suspend fun redundantSuspend() {
|
||||
println("hello world")
|
||||
@Test
|
||||
fun `does not report when private`() {
|
||||
val code = """
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
suspend fun suspendCoroutine() = suspendCoroutine { continuation: Continuation<String> ->
|
||||
continuation.resume("string")
|
||||
}
|
||||
|
||||
suspend fun doesSuspend() {
|
||||
suspendCoroutine()
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report when public function returns expression of platform type`() {
|
||||
val code = """
|
||||
class RedundantClass {
|
||||
open suspend fun redundantSuspend() {
|
||||
println("hello world")
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report suspend function without body`() {
|
||||
val code = """
|
||||
interface SuspendInterface {
|
||||
suspend fun empty()
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report overridden suspend function`() {
|
||||
val code = """
|
||||
interface SuspendInterface {
|
||||
suspend fun empty()
|
||||
}
|
||||
|
||||
class SuspendClass : SuspendInterface {
|
||||
override suspend fun empty() {
|
||||
println("hello world")
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ignores when iterator is suspending`() {
|
||||
val code = """
|
||||
class SuspendingIterator {
|
||||
suspend operator fun iterator(): Iterator<Any> = iterator { yield("value") }
|
||||
}
|
||||
|
||||
suspend fun bar() {
|
||||
for (x in SuspendingIterator()) {
|
||||
println(x)
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ignores when suspending function used in property delegate`() {
|
||||
val code = """
|
||||
class SuspendingIterator {
|
||||
suspend operator fun iterator(): Iterator<Any> = iterator { yield("value") }
|
||||
}
|
||||
|
||||
fun coroutine(block: suspend () -> Unit) {}
|
||||
|
||||
suspend fun bar() {
|
||||
val lazyValue: String by lazy {
|
||||
coroutine {
|
||||
SuspendingIterator().iterator()
|
||||
}
|
||||
"Hello"
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report when private`() {
|
||||
val code = """
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
suspend fun suspendCoroutine() = suspendCoroutine { continuation: Continuation<String> ->
|
||||
continuation.resume("string")
|
||||
}
|
||||
|
||||
suspend fun doesSuspend() {
|
||||
suspendCoroutine()
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report when public function returns expression of platform type`() {
|
||||
val code = """
|
||||
class RedundantClass {
|
||||
open suspend fun redundantSuspend() {
|
||||
println("hello world")
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report suspend function without body`() {
|
||||
val code = """
|
||||
interface SuspendInterface {
|
||||
suspend fun empty()
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report overridden suspend function`() {
|
||||
val code = """
|
||||
interface SuspendInterface {
|
||||
suspend fun empty()
|
||||
}
|
||||
|
||||
class SuspendClass : SuspendInterface {
|
||||
override suspend fun empty() {
|
||||
println("hello world")
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ignores when iterator is suspending`() {
|
||||
val code = """
|
||||
class SuspendingIterator {
|
||||
suspend operator fun iterator(): Iterator<Any> = iterator { yield("value") }
|
||||
}
|
||||
|
||||
suspend fun bar() {
|
||||
for (x in SuspendingIterator()) {
|
||||
println(x)
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ignores when suspending function used in property delegate`() {
|
||||
val code = """
|
||||
class SuspendingIterator {
|
||||
suspend operator fun iterator(): Iterator<Any> = iterator { yield("value") }
|
||||
}
|
||||
|
||||
fun coroutine(block: suspend () -> Unit) {}
|
||||
|
||||
suspend fun bar() {
|
||||
val lazyValue: String by lazy {
|
||||
coroutine {
|
||||
SuspendingIterator().iterator()
|
||||
}
|
||||
"Hello"
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@Suppress("BlockingMethodInNonBlockingContext", "RedundantSuspendModifier")
|
||||
@@ -15,64 +14,61 @@ class SleepInsteadOfDelaySpec(val env: KotlinCoreEnvironment) {
|
||||
|
||||
val subject = SleepInsteadOfDelay(Config.empty)
|
||||
|
||||
@Nested
|
||||
inner class `SleepInsteadOfDelay rule` {
|
||||
@Test
|
||||
fun `should report no issue for delay() in suspend functions`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.delay
|
||||
@Test
|
||||
fun `should report no issue for delay() in suspend functions`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
suspend fun foo() {
|
||||
delay(1000L)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(0)
|
||||
}
|
||||
suspend fun foo() {
|
||||
delay(1000L)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should report Thread.sleep() in suspend functions")
|
||||
fun reportThreadSleepInSuspendFunctions() {
|
||||
val code = """
|
||||
suspend fun foo() {
|
||||
@Test
|
||||
@DisplayName("should report Thread.sleep() in suspend functions")
|
||||
fun reportThreadSleepInSuspendFunctions() {
|
||||
val code = """
|
||||
suspend fun foo() {
|
||||
Thread.sleep(1000L)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should report Thread.sleep() in CoroutineScope.launch()")
|
||||
fun reportThreadSleepInCoroutineScopeLaunch() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
fun foo() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
Thread.sleep(1000L)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1)
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should report Thread.sleep() in CoroutineScope.launch()")
|
||||
fun reportThreadSleepInCoroutineScopeLaunch() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
fun foo() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
Thread.sleep(1000L)
|
||||
}
|
||||
@Test
|
||||
@DisplayName("should report Thread.sleep() in CoroutineScope.async()")
|
||||
fun reportThreadSleepInCoroutineScopeAsync() {
|
||||
@Suppress("DeferredResultUnused")
|
||||
val code = """
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
|
||||
fun foo() {
|
||||
CoroutineScope(Dispatchers.IO).async {
|
||||
Thread.sleep(1000L)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should report Thread.sleep() in CoroutineScope.async()")
|
||||
fun reportThreadSleepInCoroutineScopeAsync() {
|
||||
@Suppress("DeferredResultUnused")
|
||||
val code = """
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
|
||||
fun foo() {
|
||||
CoroutineScope(Dispatchers.IO).async {
|
||||
Thread.sleep(1000L)
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1)
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import io.gitlab.arturbosch.detekt.rules.KotlinCoreEnvironmentTest
|
||||
import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@KotlinCoreEnvironmentTest
|
||||
@@ -13,18 +12,118 @@ class SuspendFunWithFlowReturnTypeSpec(val env: KotlinCoreEnvironment) {
|
||||
|
||||
val subject = SuspendFunWithFlowReturnType(Config.empty)
|
||||
|
||||
@Nested
|
||||
inner class `SuspendFunWithFlowReturn` {
|
||||
@Test
|
||||
fun `reports when top-level suspend function has explicit Flow return type`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.yield
|
||||
|
||||
@Test
|
||||
fun `reports when top-level suspend function has explicit Flow return type`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.yield
|
||||
suspend fun flowValues(): Flow<Long> {
|
||||
yield()
|
||||
return flowOf(1L, 2L, 3L)
|
||||
}
|
||||
|
||||
suspend fun stateFlowValues(): StateFlow<Long> {
|
||||
yield()
|
||||
return MutableStateFlow(value = 1L)
|
||||
}
|
||||
|
||||
suspend fun mutableStateFlowValues(): MutableStateFlow<Long> {
|
||||
yield()
|
||||
return MutableStateFlow(value = 1L)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports when top-level suspend function has explicit Flow return type and star import used`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.yield
|
||||
|
||||
suspend fun flowValues(): Flow<Long> {
|
||||
yield()
|
||||
return flowOf(1L, 2L, 3L)
|
||||
}
|
||||
|
||||
suspend fun stateFlowValues(): StateFlow<Long> {
|
||||
yield()
|
||||
return MutableStateFlow(value = 1L)
|
||||
}
|
||||
|
||||
suspend fun mutableStateFlowValues(): MutableStateFlow<Long> {
|
||||
yield()
|
||||
return MutableStateFlow(value = 1L)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports when top-level suspend function has explicit FQN Flow return type`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.yield
|
||||
|
||||
suspend fun flowValues(): kotlinx.coroutines.flow.Flow<Long> {
|
||||
yield()
|
||||
return kotlinx.coroutines.flow.flowOf(1L, 2L, 3L)
|
||||
}
|
||||
|
||||
suspend fun stateFlowValues(): kotlinx.coroutines.flow.StateFlow<Long> {
|
||||
yield()
|
||||
return kotlinx.coroutines.flow.MutableStateFlow(value = 1L)
|
||||
}
|
||||
|
||||
suspend fun mutableStateFlowValues(): kotlinx.coroutines.flow.MutableStateFlow<Long> {
|
||||
yield()
|
||||
return kotlinx.coroutines.flow.MutableStateFlow(value = 1L)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports when top-level suspend function has implicit Flow return type`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
suspend fun flowValues() = flowOf(1L, 2L, 3L)
|
||||
suspend fun mutableStateFlowValues() = MutableStateFlow(value = 1L)
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports when interface suspend function has explicit Flow return type`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
interface ValuesRepository {
|
||||
suspend fun flowValues(): Flow<Long>
|
||||
suspend fun stateFlowValues(): StateFlow<Long>
|
||||
suspend fun mutableStateFlowValues(): MutableStateFlow<Long>
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports when class suspend function has explicit Flow return type`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.yield
|
||||
|
||||
class ValuesRepository {
|
||||
suspend fun flowValues(): Flow<Long> {
|
||||
yield()
|
||||
return flowOf(1L, 2L, 3L)
|
||||
@@ -39,246 +138,142 @@ class SuspendFunWithFlowReturnTypeSpec(val env: KotlinCoreEnvironment) {
|
||||
yield()
|
||||
return MutableStateFlow(value = 1L)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(3)
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports when top-level suspend function has explicit Flow return type and star import used`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.yield
|
||||
|
||||
suspend fun flowValues(): Flow<Long> {
|
||||
yield()
|
||||
return flowOf(1L, 2L, 3L)
|
||||
}
|
||||
|
||||
suspend fun stateFlowValues(): StateFlow<Long> {
|
||||
yield()
|
||||
return MutableStateFlow(value = 1L)
|
||||
}
|
||||
|
||||
suspend fun mutableStateFlowValues(): MutableStateFlow<Long> {
|
||||
yield()
|
||||
return MutableStateFlow(value = 1L)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports when top-level suspend function has explicit FQN Flow return type`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.yield
|
||||
|
||||
suspend fun flowValues(): kotlinx.coroutines.flow.Flow<Long> {
|
||||
yield()
|
||||
return kotlinx.coroutines.flow.flowOf(1L, 2L, 3L)
|
||||
}
|
||||
|
||||
suspend fun stateFlowValues(): kotlinx.coroutines.flow.StateFlow<Long> {
|
||||
yield()
|
||||
return kotlinx.coroutines.flow.MutableStateFlow(value = 1L)
|
||||
}
|
||||
|
||||
suspend fun mutableStateFlowValues(): kotlinx.coroutines.flow.MutableStateFlow<Long> {
|
||||
yield()
|
||||
return kotlinx.coroutines.flow.MutableStateFlow(value = 1L)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports when top-level suspend function has implicit Flow return type`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@Test
|
||||
fun `reports when class suspend function has implicit Flow return type`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
class ValuesRepository {
|
||||
suspend fun flowValues() = flowOf(1L, 2L, 3L)
|
||||
suspend fun mutableStateFlowValues() = MutableStateFlow(value = 1L)
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(2)
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports when interface suspend function has explicit Flow return type`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
@Test
|
||||
fun `reports when suspend extension function has explicit Flow return type`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.yield
|
||||
|
||||
interface ValuesRepository {
|
||||
suspend fun flowValues(): Flow<Long>
|
||||
suspend fun stateFlowValues(): StateFlow<Long>
|
||||
suspend fun mutableStateFlowValues(): MutableStateFlow<Long>
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(3)
|
||||
}
|
||||
suspend fun Long.flowValues(): Flow<Long> {
|
||||
yield()
|
||||
return (0..this).asFlow()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports when class suspend function has explicit Flow return type`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.yield
|
||||
suspend fun Long.stateFlowValues(): StateFlow<Long> {
|
||||
yield()
|
||||
return MutableStateFlow(value = this)
|
||||
}
|
||||
|
||||
class ValuesRepository {
|
||||
suspend fun flowValues(): Flow<Long> {
|
||||
yield()
|
||||
return flowOf(1L, 2L, 3L)
|
||||
}
|
||||
suspend fun Long.mutableStateFlowValues(): MutableStateFlow<Long> {
|
||||
yield()
|
||||
return MutableStateFlow(value = this)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(3)
|
||||
}
|
||||
|
||||
suspend fun stateFlowValues(): StateFlow<Long> {
|
||||
yield()
|
||||
return MutableStateFlow(value = 1L)
|
||||
}
|
||||
@Test
|
||||
fun `reports when suspend extension function has implicit Flow return type`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
suspend fun mutableStateFlowValues(): MutableStateFlow<Long> {
|
||||
yield()
|
||||
return MutableStateFlow(value = 1L)
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(3)
|
||||
}
|
||||
suspend fun Long.flowValues() = (0..this).asFlow()
|
||||
suspend fun Long.mutableStateFlowValues() = MutableStateFlow(value = this)
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports when class suspend function has implicit Flow return type`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@Test
|
||||
fun `does not report when suspend lambda has Flow return type`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
class ValuesRepository {
|
||||
suspend fun flowValues() = flowOf(1L, 2L, 3L)
|
||||
suspend fun mutableStateFlowValues() = MutableStateFlow(value = 1L)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(2)
|
||||
}
|
||||
fun doSomething1(block: suspend () -> Flow<Long>) {
|
||||
TODO()
|
||||
}
|
||||
fun doSomething2(block: suspend () -> StateFlow<Long>) {
|
||||
TODO()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports when suspend extension function has explicit Flow return type`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.yield
|
||||
fun doSomething3(block: suspend () -> MutableStateFlow<Long>) {
|
||||
TODO()
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
|
||||
suspend fun Long.flowValues(): Flow<Long> {
|
||||
yield()
|
||||
return (0..this).asFlow()
|
||||
}
|
||||
@Test
|
||||
fun `does not report when suspend functions have non-Flow return types`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
suspend fun Long.stateFlowValues(): StateFlow<Long> {
|
||||
yield()
|
||||
return MutableStateFlow(value = this)
|
||||
}
|
||||
suspend fun delayValue(value: Long): Long {
|
||||
delay(1_000L)
|
||||
return value
|
||||
}
|
||||
|
||||
suspend fun Long.mutableStateFlowValues(): MutableStateFlow<Long> {
|
||||
yield()
|
||||
return MutableStateFlow(value = this)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(3)
|
||||
}
|
||||
suspend fun delayValue2(value: Long) = value.apply { delay(1_000L) }
|
||||
|
||||
@Test
|
||||
fun `reports when suspend extension function has implicit Flow return type`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
suspend fun Long.delayValue(): Long {
|
||||
delay(1_000L)
|
||||
return this
|
||||
}
|
||||
|
||||
suspend fun Long.flowValues() = (0..this).asFlow()
|
||||
suspend fun Long.mutableStateFlowValues() = MutableStateFlow(value = this)
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).hasSize(2)
|
||||
}
|
||||
suspend fun Long.delayValue2() = this.apply { delay(1_000L) }
|
||||
|
||||
@Test
|
||||
fun `does not report when suspend lambda has Flow return type`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
interface ValueRepository {
|
||||
suspend fun getValue(): Long
|
||||
}
|
||||
|
||||
fun doSomething1(block: suspend () -> Flow<Long>) {
|
||||
TODO()
|
||||
}
|
||||
fun doSomething2(block: suspend () -> StateFlow<Long>) {
|
||||
TODO()
|
||||
}
|
||||
|
||||
fun doSomething3(block: suspend () -> MutableStateFlow<Long>) {
|
||||
TODO()
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report when suspend functions have non-Flow return types`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
suspend fun delayValue(value: Long): Long {
|
||||
class ValueRepository2 {
|
||||
suspend fun getValue(): Long {
|
||||
delay(1_000L)
|
||||
return value
|
||||
return 5L
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun delayValue2(value: Long) = value.apply { delay(1_000L) }
|
||||
class ValueRepository3 {
|
||||
suspend fun getValue() = 5L.apply { delay(1_000L) }
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
|
||||
suspend fun Long.delayValue(): Long {
|
||||
delay(1_000L)
|
||||
return this
|
||||
}
|
||||
@Test
|
||||
fun `does not report when non-suspend functions have Flow return types`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
suspend fun Long.delayValue2() = this.apply { delay(1_000L) }
|
||||
fun flowValues(): Flow<Long> {
|
||||
return flowOf(1L, 2L, 3L)
|
||||
}
|
||||
|
||||
interface ValueRepository {
|
||||
suspend fun getValue(): Long
|
||||
}
|
||||
fun stateFlowValues(): StateFlow<Long> {
|
||||
return MutableStateFlow(value = 1L)
|
||||
}
|
||||
|
||||
class ValueRepository2 {
|
||||
suspend fun getValue(): Long {
|
||||
delay(1_000L)
|
||||
return 5L
|
||||
}
|
||||
}
|
||||
|
||||
class ValueRepository3 {
|
||||
suspend fun getValue() = 5L.apply { delay(1_000L) }
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report when non-suspend functions have Flow return types`() {
|
||||
val code = """
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
fun flowValues(): Flow<Long> {
|
||||
return flowOf(1L, 2L, 3L)
|
||||
}
|
||||
|
||||
fun stateFlowValues(): StateFlow<Long> {
|
||||
return MutableStateFlow(value = 1L)
|
||||
}
|
||||
|
||||
fun mutableStateFlowValues(): MutableStateFlow<Long> {
|
||||
return MutableStateFlow(value = 1L)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
fun mutableStateFlowValues(): MutableStateFlow<Long> {
|
||||
return MutableStateFlow(value = 1L)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLintWithContext(env, code)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,180 +18,164 @@ import java.net.URI
|
||||
|
||||
class AbsentOrWrongFileLicenseSpec {
|
||||
|
||||
@Test
|
||||
fun `file with correct license header reports nothing`() {
|
||||
val findings = checkLicence(
|
||||
"""
|
||||
/* LICENSE */
|
||||
package cases
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `file with incorrect license header reports missed license header`() {
|
||||
val findings = checkLicence(
|
||||
"""
|
||||
/* WRONG LICENSE */
|
||||
package cases
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `file with absent license header reports missed license header`() {
|
||||
val findings = checkLicence(
|
||||
"""
|
||||
package cases
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `AbsentOrWrongFileLicense rule` {
|
||||
inner class `file with correct license header using regex matching` {
|
||||
|
||||
@Nested
|
||||
inner class `file with correct license header` {
|
||||
@Test
|
||||
fun `reports nothing for 2016`() {
|
||||
val findings = checkLicence(
|
||||
"""
|
||||
//
|
||||
// Copyright 2016 Artur Bosch & Contributors
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
package cases
|
||||
""".trimIndent(),
|
||||
isRegexLicense = true
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `reports nothing`() {
|
||||
val findings = checkLicence(
|
||||
"""
|
||||
/* LICENSE */
|
||||
package cases
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `file with incorrect license header` {
|
||||
@Test
|
||||
fun `reports nothing for 2021`() {
|
||||
val findings = checkLicence(
|
||||
"""
|
||||
//
|
||||
// Copyright 2021 Artur Bosch & Contributors
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
package cases
|
||||
""".trimIndent(),
|
||||
isRegexLicense = true
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `reports missed license header`() {
|
||||
val findings = checkLicence(
|
||||
"""
|
||||
/* WRONG LICENSE */
|
||||
package cases
|
||||
"""
|
||||
)
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
@Nested
|
||||
inner class `file with incorrect license header using regex matching` {
|
||||
|
||||
@Test
|
||||
fun `file with missing license header`() {
|
||||
val findings = checkLicence(
|
||||
"""
|
||||
package cases
|
||||
""".trimIndent(),
|
||||
isRegexLicense = true
|
||||
)
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `file with absent license header` {
|
||||
@Test
|
||||
fun `file with license header not on the first line`() {
|
||||
val findings = checkLicence(
|
||||
"""
|
||||
package cases
|
||||
//
|
||||
// Copyright 2021 Artur Bosch & Contributors
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
""".trimIndent(),
|
||||
isRegexLicense = true
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `reports missed license header`() {
|
||||
val findings = checkLicence(
|
||||
"""
|
||||
package cases
|
||||
"""
|
||||
)
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `file with correct license header using regex matching` {
|
||||
@Test
|
||||
fun `file with incomplete license header`() {
|
||||
val findings = checkLicence(
|
||||
"""
|
||||
//
|
||||
// Copyright 2021 Artur Bosch & Contributors
|
||||
//
|
||||
package cases
|
||||
""".trimIndent(),
|
||||
isRegexLicense = true
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `reports nothing for 2016`() {
|
||||
val findings = checkLicence(
|
||||
"""
|
||||
//
|
||||
// Copyright 2016 Artur Bosch & Contributors
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
package cases
|
||||
""".trimIndent(),
|
||||
isRegexLicense = true
|
||||
)
|
||||
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports nothing for 2021`() {
|
||||
val findings = checkLicence(
|
||||
"""
|
||||
//
|
||||
// Copyright 2021 Artur Bosch & Contributors
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
package cases
|
||||
""".trimIndent(),
|
||||
isRegexLicense = true
|
||||
)
|
||||
|
||||
assertThat(findings).isEmpty()
|
||||
}
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `file with incorrect license header using regex matching` {
|
||||
@Test
|
||||
fun `file with too many empty likes in license header`() {
|
||||
val findings = checkLicence(
|
||||
"""
|
||||
//
|
||||
//
|
||||
// Copyright 2021 Artur Bosch & Contributors
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
package cases
|
||||
""".trimIndent(),
|
||||
isRegexLicense = true
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `file with missing license header`() {
|
||||
val findings = checkLicence(
|
||||
"""
|
||||
package cases
|
||||
""".trimIndent(),
|
||||
isRegexLicense = true
|
||||
)
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
@Test
|
||||
fun `file with incorrect year in license header`() {
|
||||
val findings = checkLicence(
|
||||
"""
|
||||
//
|
||||
// Copyright 202 Artur Bosch & Contributors
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
package cases
|
||||
""".trimIndent(),
|
||||
isRegexLicense = true
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `file with license header not on the first line`() {
|
||||
val findings = checkLicence(
|
||||
"""
|
||||
package cases
|
||||
//
|
||||
// Copyright 2021 Artur Bosch & Contributors
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
""".trimIndent(),
|
||||
isRegexLicense = true
|
||||
)
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `file with incomplete license header`() {
|
||||
val findings = checkLicence(
|
||||
"""
|
||||
//
|
||||
// Copyright 2021 Artur Bosch & Contributors
|
||||
//
|
||||
package cases
|
||||
""".trimIndent(),
|
||||
isRegexLicense = true
|
||||
)
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `file with too many empty likes in license header`() {
|
||||
val findings = checkLicence(
|
||||
"""
|
||||
//
|
||||
//
|
||||
// Copyright 2021 Artur Bosch & Contributors
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
package cases
|
||||
""".trimIndent(),
|
||||
isRegexLicense = true
|
||||
)
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `file with incorrect year in license header`() {
|
||||
val findings = checkLicence(
|
||||
"""
|
||||
//
|
||||
// Copyright 202 Artur Bosch & Contributors
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
package cases
|
||||
""".trimIndent(),
|
||||
isRegexLicense = true
|
||||
)
|
||||
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
assertThat(findings).hasSize(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,50 +2,45 @@ package io.gitlab.arturbosch.detekt.rules.documentation
|
||||
|
||||
import io.gitlab.arturbosch.detekt.test.compileAndLint
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class CommentOverPrivateMethodSpec {
|
||||
val subject = CommentOverPrivateFunction()
|
||||
|
||||
@Nested
|
||||
inner class `CommentOverPrivateFunction rule` {
|
||||
@Test
|
||||
fun `reports private method with a comment`() {
|
||||
val code = """
|
||||
class Test {
|
||||
/**
|
||||
* asdf
|
||||
*/
|
||||
private fun f() {}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports private method with a comment`() {
|
||||
val code = """
|
||||
class Test {
|
||||
/**
|
||||
* asdf
|
||||
*/
|
||||
private fun f() {}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
@Test
|
||||
fun `does not report public method with a comment`() {
|
||||
val code = """
|
||||
/**
|
||||
* asdf
|
||||
*/
|
||||
fun f() {}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report public method with a comment`() {
|
||||
val code = """
|
||||
@Test
|
||||
fun `does not report public method in a class with a comment`() {
|
||||
val code = """
|
||||
class Test {
|
||||
/**
|
||||
* asdf
|
||||
*/
|
||||
fun f() {}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report public method in a class with a comment`() {
|
||||
val code = """
|
||||
class Test {
|
||||
/**
|
||||
* asdf
|
||||
*/
|
||||
fun f() {}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,61 +2,56 @@ package io.gitlab.arturbosch.detekt.rules.documentation
|
||||
|
||||
import io.gitlab.arturbosch.detekt.test.compileAndLint
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class CommentOverPrivatePropertiesSpec {
|
||||
val subject = CommentOverPrivateProperty()
|
||||
private val subject = CommentOverPrivateProperty()
|
||||
|
||||
@Nested
|
||||
inner class `CommentOverPrivateProperty rule` {
|
||||
@Test
|
||||
fun `reports private property with a comment`() {
|
||||
val code = """
|
||||
/**
|
||||
* asdf
|
||||
*/
|
||||
private val v = 1
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports private property with a comment`() {
|
||||
val code = """
|
||||
@Test
|
||||
fun `does not report public property with a comment`() {
|
||||
val code = """
|
||||
/**
|
||||
* asdf
|
||||
*/
|
||||
val v = 1
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports private property in class with a comment`() {
|
||||
val code = """
|
||||
class Test {
|
||||
/**
|
||||
* asdf
|
||||
*/
|
||||
private val v = 1
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report public property with a comment`() {
|
||||
val code = """
|
||||
@Test
|
||||
fun `does not report public property in class with a comment`() {
|
||||
val code = """
|
||||
class Test {
|
||||
/**
|
||||
* asdf
|
||||
*/
|
||||
val v = 1
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports private property in class with a comment`() {
|
||||
val code = """
|
||||
class Test {
|
||||
/**
|
||||
* asdf
|
||||
*/
|
||||
private val v = 1
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report public property in class with a comment`() {
|
||||
val code = """
|
||||
class Test {
|
||||
/**
|
||||
* asdf
|
||||
*/
|
||||
val v = 1
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,151 +8,148 @@ import org.junit.jupiter.api.Test
|
||||
class DeprecatedBlockTagSpec {
|
||||
val subject = DeprecatedBlockTag()
|
||||
|
||||
@Test
|
||||
fun `does not report regular kdoc block`() {
|
||||
val code = """
|
||||
/**
|
||||
* This is just a regular kdoc block.
|
||||
*
|
||||
* Nothing to see here...
|
||||
*/
|
||||
val v = 2
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(0)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `DeprecatedBlockTag rule` {
|
||||
inner class `reporting deprecation tag on kdoc block` {
|
||||
val code = """
|
||||
/**
|
||||
* I am a KDoc block
|
||||
*
|
||||
* @deprecated oh no, this should not be here
|
||||
*/
|
||||
fun ohNo() { }
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `does not report regular kdoc block`() {
|
||||
val code = """
|
||||
/**
|
||||
* This is just a regular kdoc block.
|
||||
*
|
||||
* Nothing to see here...
|
||||
*/
|
||||
val v = 2
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(0)
|
||||
fun `has found something`() {
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `reporting deprecation tag on kdoc block` {
|
||||
@Test
|
||||
fun `correct message`() {
|
||||
assertThat(subject.compileAndLint(code)[0]).hasMessage(
|
||||
"@deprecated tag block does not properly report " +
|
||||
"deprecation in Kotlin, use @Deprecated annotation instead"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `reporting deprecation tag wherever @Deprecated is available` {
|
||||
|
||||
@Test
|
||||
fun `report deprecation tag on class`() {
|
||||
val code = """
|
||||
/**
|
||||
* I am a KDoc block
|
||||
*
|
||||
* @deprecated oh no, this should not be here
|
||||
*/
|
||||
fun ohNo() { }
|
||||
/**
|
||||
* Hello there
|
||||
*
|
||||
* @deprecated This thing is deprecated
|
||||
*/
|
||||
class Thing { }
|
||||
"""
|
||||
|
||||
@Test
|
||||
fun `has found something`() {
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `correct message`() {
|
||||
assertThat(subject.compileAndLint(code)[0]).hasMessage(
|
||||
"@deprecated tag block does not properly report " +
|
||||
"deprecation in Kotlin, use @Deprecated annotation instead"
|
||||
)
|
||||
}
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `reporting deprecation tag wherever @Deprecated is available` {
|
||||
|
||||
@Test
|
||||
fun `report deprecation tag on class`() {
|
||||
val code = """
|
||||
@Test
|
||||
fun `report deprecation tag on property`() {
|
||||
val code = """
|
||||
class Thing {
|
||||
/**
|
||||
* Hello there
|
||||
* A thing you should not use
|
||||
*
|
||||
* @deprecated This thing is deprecated
|
||||
* @deprecated Do not use that
|
||||
*/
|
||||
class Thing { }
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
val doNotUseMe = 0
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `report deprecation tag on property`() {
|
||||
val code = """
|
||||
@Test
|
||||
fun `report deprecation tag on annotation class`() {
|
||||
val code = """
|
||||
/**
|
||||
* An annotation you should not use
|
||||
*
|
||||
* @deprecated Do not use that
|
||||
*/
|
||||
annotation class Thing()
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `report deprecation tag on constructor`() {
|
||||
val code = """
|
||||
class Thing {
|
||||
/**
|
||||
* A thing you should not use
|
||||
* A constructor you should not use
|
||||
*
|
||||
* @deprecated Do not use that
|
||||
*/
|
||||
val doNotUseMe = 0
|
||||
constructor(something: String)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `report deprecation tag on annotation class`() {
|
||||
val code = """
|
||||
/**
|
||||
* An annotation you should not use
|
||||
*
|
||||
* @deprecated Do not use that
|
||||
*/
|
||||
annotation class Thing()
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `report deprecation tag on constructor`() {
|
||||
val code = """
|
||||
class Thing {
|
||||
@Test
|
||||
fun `report deprecation tag on property setter`() {
|
||||
val code = """
|
||||
class Thing {
|
||||
var someProperty: Int
|
||||
get() = 10
|
||||
/**
|
||||
* A constructor you should not use
|
||||
*
|
||||
* @deprecated Do not use that
|
||||
* Do not use this setter
|
||||
*
|
||||
* @deprecated Do not use it
|
||||
*/
|
||||
constructor(something: String)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
set(value) { println(value) }
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `report deprecation tag on property setter`() {
|
||||
val code = """
|
||||
class Thing {
|
||||
var someProperty: Int
|
||||
get() = 10
|
||||
/**
|
||||
* Do not use this setter
|
||||
*
|
||||
* @deprecated Do not use it
|
||||
*/
|
||||
set(value) { println(value) }
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
@Test
|
||||
fun `report deprecation tag on property getter`() {
|
||||
val code = """
|
||||
class Thing {
|
||||
var someProperty: Int
|
||||
/**
|
||||
* Do not use this getter
|
||||
*
|
||||
* @deprecated Do not use it
|
||||
*/
|
||||
get() = 10
|
||||
set(value) { println(value) }
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `report deprecation tag on property getter`() {
|
||||
val code = """
|
||||
class Thing {
|
||||
var someProperty: Int
|
||||
/**
|
||||
* Do not use this getter
|
||||
*
|
||||
* @deprecated Do not use it
|
||||
*/
|
||||
get() = 10
|
||||
set(value) { println(value) }
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `report deprecation tag on typealias`() {
|
||||
val code = """
|
||||
/**
|
||||
* This alias is pointless, do not use it
|
||||
*
|
||||
* @deprecated Do not use this typealias
|
||||
*/
|
||||
typealias VeryString = String
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
@Test
|
||||
fun `report deprecation tag on typealias`() {
|
||||
val code = """
|
||||
/**
|
||||
* This alias is pointless, do not use it
|
||||
*
|
||||
* @deprecated Do not use this typealias
|
||||
*/
|
||||
typealias VeryString = String
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,209 +2,204 @@ package io.gitlab.arturbosch.detekt.rules.documentation
|
||||
|
||||
import io.gitlab.arturbosch.detekt.test.compileAndLint
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class EndOfSentenceFormatSpec {
|
||||
val subject = EndOfSentenceFormat()
|
||||
|
||||
@Nested
|
||||
inner class `KDocStyle rule` {
|
||||
@Test
|
||||
fun `reports invalid KDoc endings on classes`() {
|
||||
val code = """
|
||||
/** Some doc */
|
||||
class Test {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports invalid KDoc endings on classes`() {
|
||||
val code = """
|
||||
@Test
|
||||
fun `reports invalid KDoc endings on function with expression body`() {
|
||||
val code = """
|
||||
/** Some doc */
|
||||
class Test {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
fun f(x: Int, y: Int, z: Int) =
|
||||
if (x == 0) y + z else x + y
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports invalid KDoc endings on function with expression body`() {
|
||||
val code = """
|
||||
/** Some doc */
|
||||
fun f(x: Int, y: Int, z: Int) =
|
||||
if (x == 0) y + z else x + y
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
@Test
|
||||
fun `reports invalid KDoc endings on properties`() {
|
||||
val code = """
|
||||
class Test {
|
||||
/** Some doc */
|
||||
val test = 3
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports invalid KDoc endings on properties`() {
|
||||
val code = """
|
||||
class Test {
|
||||
/** Some doc */
|
||||
val test = 3
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
@Test
|
||||
fun `reports invalid KDoc endings on top-level functions`() {
|
||||
val code = """
|
||||
/** Some doc */
|
||||
fun test() = 3
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports invalid KDoc endings on top-level functions`() {
|
||||
val code = """
|
||||
@Test
|
||||
fun `reports invalid KDoc endings on functions`() {
|
||||
val code = """
|
||||
class Test {
|
||||
/** Some doc */
|
||||
fun test() = 3
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports invalid KDoc endings`() {
|
||||
val code = """
|
||||
class Test {
|
||||
/** Some doc-- */
|
||||
fun test() = 3
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports invalid KDoc endings in block`() {
|
||||
val code = """
|
||||
/**
|
||||
* Something off abc@@
|
||||
*/
|
||||
class Test {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not validate first sentence KDoc endings in a multi sentence comment`() {
|
||||
val code = """
|
||||
/**
|
||||
* This sentence is correct.
|
||||
*
|
||||
* This sentence doesn't matter
|
||||
*/
|
||||
class Test {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report KDoc which doesn't contain any real sentence`() {
|
||||
val code = """
|
||||
/**
|
||||
*/
|
||||
class Test {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report KDoc which doesn't contain any real sentence but many tags`() {
|
||||
val code = """
|
||||
/**
|
||||
* @configuration this - just an example (default: `150`)
|
||||
*
|
||||
* @active since v1.0.0
|
||||
*/
|
||||
class Test {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report KDoc which doesn't contain any real sentence but html tags`() {
|
||||
val code = """
|
||||
/**
|
||||
*
|
||||
* <noncompliant>
|
||||
* fun foo(): Unit { }
|
||||
* </noncompliant>
|
||||
*
|
||||
* <compliant>
|
||||
* fun foo() { }
|
||||
* </compliant>
|
||||
*
|
||||
*/
|
||||
class Test {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report KDoc ending with periods`() {
|
||||
val code = """
|
||||
/**
|
||||
* Something correct.
|
||||
*/
|
||||
class Test {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report KDoc ending with questionmarks`() {
|
||||
val code = """
|
||||
/**
|
||||
* Something correct?
|
||||
*/
|
||||
class Test {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report KDoc ending with exclamation marks`() {
|
||||
val code = """
|
||||
/**
|
||||
* Something correct!
|
||||
*/
|
||||
class Test {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report KDoc ending with colon`() {
|
||||
val code = """
|
||||
/**
|
||||
* Something correct:
|
||||
*/
|
||||
class Test {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report URLs in comments`() {
|
||||
val code = """
|
||||
/** http://www.google.com */
|
||||
class Test1 {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports invalid KDoc endings on functions`() {
|
||||
val code = """
|
||||
class Test {
|
||||
/** Some doc */
|
||||
fun test() = 3
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports invalid KDoc endings`() {
|
||||
val code = """
|
||||
class Test {
|
||||
/** Some doc-- */
|
||||
fun test() = 3
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `reports invalid KDoc endings in block`() {
|
||||
val code = """
|
||||
/**
|
||||
* Something off abc@@
|
||||
*/
|
||||
class Test {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not validate first sentence KDoc endings in a multi sentence comment`() {
|
||||
val code = """
|
||||
/**
|
||||
* This sentence is correct.
|
||||
*
|
||||
* This sentence doesn't matter
|
||||
*/
|
||||
class Test {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report KDoc which doesn't contain any real sentence`() {
|
||||
val code = """
|
||||
/**
|
||||
*/
|
||||
class Test {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report KDoc which doesn't contain any real sentence but many tags`() {
|
||||
val code = """
|
||||
/**
|
||||
* @configuration this - just an example (default: `150`)
|
||||
*
|
||||
* @active since v1.0.0
|
||||
*/
|
||||
class Test {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report KDoc which doesn't contain any real sentence but html tags`() {
|
||||
val code = """
|
||||
/**
|
||||
*
|
||||
* <noncompliant>
|
||||
* fun foo(): Unit { }
|
||||
* </noncompliant>
|
||||
*
|
||||
* <compliant>
|
||||
* fun foo() { }
|
||||
* </compliant>
|
||||
*
|
||||
*/
|
||||
class Test {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report KDoc ending with periods`() {
|
||||
val code = """
|
||||
/**
|
||||
* Something correct.
|
||||
*/
|
||||
class Test {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report KDoc ending with questionmarks`() {
|
||||
val code = """
|
||||
/**
|
||||
* Something correct?
|
||||
*/
|
||||
class Test {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report KDoc ending with exclamation marks`() {
|
||||
val code = """
|
||||
/**
|
||||
* Something correct!
|
||||
*/
|
||||
class Test {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report KDoc ending with colon`() {
|
||||
val code = """
|
||||
/**
|
||||
* Something correct:
|
||||
*/
|
||||
class Test {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `does not report URLs in comments`() {
|
||||
val code = """
|
||||
/** http://www.google.com */
|
||||
class Test1 {
|
||||
}
|
||||
|
||||
/** Look here
|
||||
http://google.com */
|
||||
class Test2 {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
/** Look here
|
||||
http://google.com */
|
||||
class Test2 {
|
||||
}
|
||||
"""
|
||||
assertThat(subject.compileAndLint(code)).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user