Extract xml and html reports to own modules (#2750)

* Move xml report to own module

* Extract common complexity report logic to metrics module

This allows to share complexity report logic between the console report and a report-html module.

* Move whichXXX functions from DebugUtils to api module for now

More modules use the detekt version at runtime
and it is nice to have it in plugins as the IntelliJ or Sonar plugin. We should later move this helpers to the tooling-api.

* Extract html report to own module

* Merge all service files when packaging cli fatJar

* Write the detekt version to all jar manifest files

This allows to retrieve the detekt version from any jar at runtime.

* Extract txt report to own module
This commit is contained in:
Artur Bosch
2020-05-31 15:35:38 +02:00
committed by GitHub
parent 12bce58366
commit 65a49589e4
45 changed files with 214 additions and 121 deletions

View File

@@ -1,4 +1,4 @@
dependencies {
api(project(":detekt-api"))
testImplementation(project(":detekt-test-utils"))
testImplementation(project(":detekt-test"))
}

View File

@@ -0,0 +1,19 @@
package io.github.detekt.metrics
import io.github.detekt.metrics.processors.commentLinesKey
import io.github.detekt.metrics.processors.complexityKey
import io.github.detekt.metrics.processors.linesKey
import io.github.detekt.metrics.processors.logicalLinesKey
import io.github.detekt.metrics.processors.sourceLinesKey
import io.gitlab.arturbosch.detekt.api.Detektion
class ComplexityMetric(detektion: Detektion) {
val mcc = detektion.getData(complexityKey)
val cognitiveComplexity = detektion.getData(CognitiveComplexity.KEY)
val loc = detektion.getData(linesKey)
val sloc = detektion.getData(sourceLinesKey)
val lloc = detektion.getData(logicalLinesKey)
val cloc = detektion.getData(commentLinesKey)
val findings = detektion.findings.entries
}

View File

@@ -0,0 +1,52 @@
package io.github.detekt.metrics
import io.gitlab.arturbosch.detekt.api.Detektion
import java.util.Locale
class ComplexityReportGenerator(private val complexityMetric: ComplexityMetric) {
private var numberOfSmells = 0
private var smellPerThousandLines = 0
private var mccPerThousandLines = 0
private var commentSourceRatio = 0
companion object Factory {
fun create(detektion: Detektion): ComplexityReportGenerator =
ComplexityReportGenerator(ComplexityMetric(detektion))
}
fun generate(): List<String>? {
if (cannotGenerate()) return null
return listOf(
"%,d lines of code (loc)".format(Locale.US, complexityMetric.loc),
"%,d source lines of code (sloc)".format(Locale.US, complexityMetric.sloc),
"%,d logical lines of code (lloc)".format(Locale.US, complexityMetric.lloc),
"%,d comment lines of code (cloc)".format(Locale.US, complexityMetric.cloc),
"%,d cyclomatic complexity (mcc)".format(Locale.US, complexityMetric.mcc),
"%,d cognitive complexity".format(Locale.US, complexityMetric.cognitiveComplexity),
"%,d number of total code smells".format(Locale.US, numberOfSmells),
"%,d%% comment source ratio".format(Locale.US, commentSourceRatio),
"%,d mcc per 1,000 lloc".format(Locale.US, mccPerThousandLines),
"%,d code smells per 1,000 lloc".format(Locale.US, smellPerThousandLines)
)
}
private fun cannotGenerate(): Boolean {
return when {
null in setOf(
complexityMetric.mcc,
complexityMetric.cloc,
complexityMetric.cognitiveComplexity
) -> true
complexityMetric.lloc == null || complexityMetric.lloc == 0 -> true
complexityMetric.sloc == null || complexityMetric.sloc == 0 -> true
else -> {
numberOfSmells = complexityMetric.findings.sumBy { it.value.size }
smellPerThousandLines = numberOfSmells * 1000 / complexityMetric.lloc
mccPerThousandLines = complexityMetric.mcc!! * 1000 / complexityMetric.lloc
commentSourceRatio = complexityMetric.cloc!! * 100 / complexityMetric.sloc
false
}
}
}
}

View File

@@ -0,0 +1,94 @@
package io.github.detekt.metrics
import io.github.detekt.metrics.processors.commentLinesKey
import io.github.detekt.metrics.processors.complexityKey
import io.github.detekt.metrics.processors.linesKey
import io.github.detekt.metrics.processors.logicalLinesKey
import io.github.detekt.metrics.processors.sourceLinesKey
import io.gitlab.arturbosch.detekt.api.Detektion
import io.gitlab.arturbosch.detekt.api.Finding
import io.gitlab.arturbosch.detekt.test.TestDetektion
import io.mockk.every
import io.mockk.mockk
import org.assertj.core.api.Assertions.assertThat
import org.spekframework.spek2.Spek
import org.spekframework.spek2.style.specification.describe
internal class ComplexityReportGeneratorSpec : Spek({
describe("complexity report generator") {
lateinit var detektion: TestDetektion
beforeEachTest {
val finding = mockk<Finding>()
every { finding.id }.returns("test")
detektion = TestDetektion(finding)
addData(detektion)
}
context("several complexity metrics") {
it("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)
}
}
context("several invalid complexity metrics") {
it("returns null for missing mcc") {
detektion.removeData(complexityKey)
assertThat(generateComplexityReport(detektion)).isNull()
}
it("returns null for missing lloc") {
detektion.removeData(logicalLinesKey)
assertThat(generateComplexityReport(detektion)).isNull()
detektion.addData(logicalLinesKey, 0)
assertThat(generateComplexityReport(detektion)).isNull()
}
it("returns null for missing sloc") {
detektion.removeData(sourceLinesKey)
assertThat(generateComplexityReport(detektion)).isNull()
detektion.addData(sourceLinesKey, 0)
assertThat(generateComplexityReport(detektion)).isNull()
}
it("returns null for missing cloc") {
detektion.removeData(complexityKey)
assertThat(generateComplexityReport(detektion)).isNull()
}
}
}
})
private fun addData(detektion: Detektion) {
detektion.addData(complexityKey, 2)
detektion.addData(CognitiveComplexity.KEY, 2)
detektion.addData(linesKey, 1000)
detektion.addData(sourceLinesKey, 6)
detektion.addData(logicalLinesKey, 5)
detektion.addData(commentLinesKey, 4)
}
private fun generateComplexityReport(detektion: Detektion): List<String>? {
val complexityMetric = ComplexityMetric(detektion)
val generator = ComplexityReportGenerator(complexityMetric)
return generator.generate()
}