Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

jacoco total coverage #12

Merged
merged 1 commit into from
Sep 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions core/src/main/java/io/github/tarek360/core/Markdown.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package io.github.tarek360.core


fun String.bold() = "**$this**"
32 changes: 32 additions & 0 deletions rules-test/src/main/java/io/github/tarek360/rules/test/TestRule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,38 @@ class TestRule internal constructor(private val rule: Rule) {
return this
}

fun assertFirstIssue(expectedIssue: Issue): TestRule {
assertHasIssueAt(expectedIssue, 0)
return this
}

fun assertSecondIssue(expectedIssue: Issue): TestRule {
assertHasIssueAt(expectedIssue, 1)
return this
}

fun assertThirdIssue(expectedIssue: Issue): TestRule {
assertHasIssueAt(expectedIssue, 2)
return this
}

fun assertLastIssue(expectedIssue: Issue): TestRule {
if (report == null || report?.issues == null || report?.issues!!.isEmpty()) {
throw RuntimeException("No issues")
}
assertHasIssueAt(expectedIssue, report?.issues?.size!! - 1)
return this
}

fun assertHasIssueAt(expectedIssue: Issue, position: Int): TestRule {
if (report == null || report?.issues == null || report?.issues!!.isEmpty()) {
throw RuntimeException("No issues")
}
val actualIssues = report?.issues?.get(position)
assertEquals(expectedIssue, actualIssues)
return this
}

fun assertHasIssue(expectedIssue: Issue): TestRule {

val issues = report?.issues
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
package io.github.tarek360.rules.coverage

import io.github.tarek360.core.bold
import io.github.tarek360.core.filterThenMapIfNotNull
import io.github.tarek360.gitdiff.GitDiff
import io.github.tarek360.gitdiff.GitFile
import io.github.tarek360.rules.core.*
import io.github.tarek360.rules.coverage.JacocoCoverageRule.JacocoCoverageRuleBuilder
import io.github.tarek360.rules.core.Issue
import io.github.tarek360.rules.core.Level.ERROR
import io.github.tarek360.rules.core.Level.INFO
import io.github.tarek360.rules.core.Report
import io.github.tarek360.rules.core.Rule
import io.github.tarek360.rules.core.RuleDsl

class JacocoCoverageRule internal constructor(
private var classCoverageThreshold: Int,
private var csvFilePath: String,
private var htmlFilePath: String?,
private val csvParser : CsvParser
private val csvParser: CsvParser
) : Rule() {


companion object {
const val REPORT_TITLE = "JaCoCo Coverage Report"
const val COVERAGE_TITLE = "Coverage"
const val PROJECT_TOTAL_COVERAGE_TITLE = "Project Total Coverage"
}

private lateinit var report: Report
Expand All @@ -33,16 +31,19 @@ class JacocoCoverageRule internal constructor(
report = Report(REPORT_TITLE, COVERAGE_TITLE)
classesCoverage = csvParser.parse(csvFilePath)

applyToFiles(gitDiff.getAddedFiles())
applyToFiles(gitDiff.getModifiedFiles())
checkFiles(gitDiff.getAddedFiles())
checkFiles(gitDiff.getModifiedFiles())

addTotalCoverage(classesCoverage.sumBy { it.coveredBranches } / classesCoverage.size)

return report.takeIf {
report.issues.isNotEmpty()
}
}

private fun applyToFiles(gitFiles: List<GitFile>) {
private fun checkFiles(gitFiles: List<GitFile>) {

gitFiles.filterThenMapIfNotNull { file ->
val touchedClasses = gitFiles.filterThenMapIfNotNull { file ->

val inKotlinFile = file.path.split("${separator}kotlin$separator")
val inJavaFile = file.path.split("${separator}java$separator")
Expand All @@ -54,41 +55,43 @@ class JacocoCoverageRule internal constructor(
null
}
}
}

}.forEach { gitFilePath ->

classesCoverage.forEach { classCoverage ->

classesCoverage.forEach { classCoverage ->
touchedClasses.forEach { gitFilePath ->
if (classCoverage.filePath == gitFilePath || classCoverage.filePath.startsWith("$gitFilePath\$")) {
//Inner class
addIssue(classCoverage, gitFilePath)
addIssue(classCoverage)
}
}
}
}

private fun addIssue(classCoverage: ClassCoverage, gitFilePath: String) {
val msg = "**${classCoverage.filePath}**"
val coverage = "**${classCoverage.coveredBranches}%**"
private fun addTotalCoverage(totalCoverage: Int) {
val level = getLevel(totalCoverage)
report.issues.add(Issue(level = level, msg = PROJECT_TOTAL_COVERAGE_TITLE.bold(), description = "$totalCoverage%".bold()))
}

private fun addIssue(classCoverage: ClassCoverage) {
val msg = classCoverage.filePath
val coverage = "${classCoverage.coveredBranches}%"

val name = classCoverage.fileName.replace('.','$')
val name = classCoverage.fileName.replace('.', '$')

val filePath = if (htmlFilePath != null) {
"$htmlFilePath$separator${classCoverage.classPackage}$separator$name.html"
} else {
"${classCoverage.classPackage}$separator$name.html"
}

when {
classCoverage.coveredBranches in 95..100 ->
report.issues.add(Issue(level = INFO("🏆"), filePath = filePath, msg = msg, description = coverage))

classCoverage.coveredBranches >= classCoverageThreshold ->
report.issues.add(Issue(level = INFO("✅"), filePath = filePath, msg = msg, description = coverage))

classCoverage.coveredBranches < classCoverageThreshold ->
report.issues.add(Issue(level = ERROR("💣"), filePath = filePath, msg = msg, description = coverage))
val level = getLevel(classCoverage.coveredBranches)
report.issues.add(Issue(level = level, filePath = filePath, msg = msg, description = coverage))
}

private fun getLevel(totalCoverage: Int): Level {
return when {
totalCoverage >= classCoverageThreshold -> INFO("✅")
totalCoverage in 1..classCoverageThreshold -> ERROR("💣")
else -> ERROR("🔥")
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package io.github.tarek360.rules.coverage

import io.github.tarek360.core.bold
import io.github.tarek360.rules.core.Issue
import io.github.tarek360.rules.core.Level
import io.github.tarek360.rules.coverage.JacocoCoverageRule.Companion.PROJECT_TOTAL_COVERAGE_TITLE
import io.github.tarek360.rules.test.testRule
import org.junit.Before
import org.junit.Test
Expand All @@ -11,6 +13,11 @@ class JacocoCoverageRuleTest {

private lateinit var csvParser: CsvParser

private val expectedTotalCoverage = Issue(
msg = PROJECT_TOTAL_COVERAGE_TITLE.bold(),
level = Level.ERROR("💣"),
description = "56%".bold())

@Before
fun setup() {

Expand All @@ -24,7 +31,7 @@ class JacocoCoverageRuleTest {
ClassCoverage("com.android.game", "Presenter",
"com/android/game/Presenter", 50),
ClassCoverage("com.android.game", "Presenter.InnerClass",
"com/android/game/Presenter\$InnerClass", 30)
"com/android/game/Presenter\$InnerClass", 0)
)
}
}
Expand All @@ -37,40 +44,36 @@ class JacocoCoverageRuleTest {

val htmlFilePath = "https://tarek360.github.io/koshry/build/reports"


val rule = JacocoCoverageRule(
classCoverageThreshold = 80,
csvFilePath = "build/reports/jacoco/jacoco.csv",
htmlFilePath = htmlFilePath,
csvParser = csvParser
)


val expectedIssue1 = Issue(
msg = "**com/android/game/App**",
level = Level.INFO("🏆"),
msg = "com/android/game/App",
level = Level.INFO(""),
filePath = "$htmlFilePath${separator}com.android.game${separator}App.html",
description = "**95%**")

description = "95%")

val expectedIssue2 = Issue(
msg = "**com/android/game/Activity**",
msg = "com/android/game/Activity",
level = Level.INFO("✅"),
filePath = "$htmlFilePath${separator}com.android.game${separator}Activity.html",
description = "**80%**")

description = "80%")

val expectedIssue3 = Issue(
msg = "**com/android/game/Presenter**",
msg = "com/android/game/Presenter",
level = Level.ERROR("💣"),
filePath = "$htmlFilePath${separator}com.android.game${separator}Presenter.html",
description = "**50%**")
description = "50%")

val expectedIssue4 = Issue(
msg = "**com/android/game/Presenter\$InnerClass**",
level = Level.ERROR("💣"),
msg = "com/android/game/Presenter\$InnerClass",
level = Level.ERROR("🔥"),
filePath = "$htmlFilePath${separator}com.android.game${separator}Presenter\$InnerClass.html",
description = "**30%**")
description = "0%")


testRule(rule)
Expand All @@ -84,11 +87,12 @@ class JacocoCoverageRuleTest {
.apply()

// Assert
.assertHasIssue(expectedIssue = expectedIssue1)
.assertHasIssue(expectedIssue = expectedIssue2)
.assertHasIssue(expectedIssue = expectedIssue3)
.assertHasIssue(expectedIssue = expectedIssue4)
.assertIssuesCount(expectedCount = 4)
.assertFirstIssue(expectedIssue = expectedIssue1)
.assertSecondIssue(expectedIssue = expectedIssue2)
.assertThirdIssue(expectedIssue = expectedIssue3)
.assertHasIssueAt(expectedIssue = expectedIssue4, position = 3)
.assertLastIssue(expectedIssue = expectedTotalCoverage)
.assertIssuesCount(expectedCount = 5)
}

@Test
Expand All @@ -103,10 +107,11 @@ class JacocoCoverageRuleTest {
)

val expectedIssue = Issue(
msg = "**com/android/game/App**",
level = Level.INFO("🏆"),
msg = "com/android/game/App",
level = Level.INFO(""),
filePath = "com.android.game${separator}App.html",
description = "**95%**")
description = "95%")


testRule(rule)
.withAddedFile(file = "src/main/java/com/android/game/App.java")
Expand All @@ -115,8 +120,9 @@ class JacocoCoverageRuleTest {
.apply()

// Assert
.assertHasIssue(expectedIssue = expectedIssue)
.assertIssuesCount(expectedCount = 1)
.assertFirstIssue(expectedIssue = expectedIssue)
.assertLastIssue(expectedIssue = expectedTotalCoverage)
.assertIssuesCount(expectedCount = 2)
}


Expand All @@ -131,7 +137,6 @@ class JacocoCoverageRuleTest {
htmlFilePath = null
)


testRule(rule)
.withModifiedFile(file = "build.gradle")
.withDeletedFile(file = "src/main/kotlin/com/android/game/CustomView.kt")
Expand All @@ -140,6 +145,6 @@ class JacocoCoverageRuleTest {
.apply()

// Assert
.assertNoIssues()
.assertIssuesCount(expectedCount = 1) // only the total coverage
}
}