Skip to content

Instantly share code, notes, and snippets.

@ethanmdavidson
Created February 9, 2021 15:18
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ethanmdavidson/a73147ce5bdcde4a87554c7303bae8f4 to your computer and use it in GitHub Desktop.
Save ethanmdavidson/a73147ce5bdcde4a87554c7303bae8f4 to your computer and use it in GitHub Desktop.
Fancy gradle test result logging, in kotlin
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
import groovy.time.TimeCategory
import java.util.Date
/**
* based on the groovy code by lwasyl:
* https://gist.github.com/lwasyl/f5b2b4ebe9e348ebbd8ee4cb995f8362
*/
var testResults by extra(mutableListOf<TestOutcome>()) // Container for tests summaries
tasks.withType<Test>().configureEach {
val testTask = this
testLogging {
events = setOf(
TestLogEvent.FAILED,
TestLogEvent.SKIPPED,
TestLogEvent.STANDARD_OUT,
TestLogEvent.STANDARD_ERROR
)
exceptionFormat = TestExceptionFormat.FULL
showExceptions = true
showCauses = true
showStackTraces = true
}
ignoreFailures = true // Always try to run all tests for all modules
//addTestListener is a workaround https://github.com/gradle/kotlin-dsl-samples/issues/836
addTestListener(object : TestListener {
override fun beforeSuite(suite: TestDescriptor) {}
override fun beforeTest(testDescriptor: TestDescriptor) {}
override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) {}
override fun afterSuite(desc: TestDescriptor, result: TestResult) {
if (desc.parent != null) return // Only summarize results for whole modules
val summary = TestOutcome().apply {
add( "${testTask.project.name}:${testTask.name} results: ${result.resultType} " +
"(" +
"${result.testCount} tests, " +
"${result.successfulTestCount} successes, " +
"${result.failedTestCount} failures, " +
"${result.skippedTestCount} skipped" +
") " +
"in ${TimeCategory.minus(Date(result.endTime), Date(result.startTime))}")
add("Report file: ${testTask.reports.html.entryPoint}")
}
// Add reports in `testsResults`, keep failed suites at the end
if (result.resultType == TestResult.ResultType.SUCCESS) {
testResults.add(0, summary)
} else {
testResults.add(summary)
}
}
})
}
gradle.buildFinished {
if (testResults.isNotEmpty()) {
printResults(testResults)
}
}
fun printResults(allResults:List<TestOutcome>) {
val maxLength = allResults.map{ it.maxWidth() }
.max() ?: 0
println("┌${"─".repeat(maxLength)}┐")
println(allResults.joinToString("├${"─".repeat(maxLength)}┤\n") { testOutcome ->
testOutcome.lines.joinToString("│\n│", "│", "│") {
it + " ".repeat(maxLength - it.length)
}
})
println("└${"─".repeat(maxLength)}┘")
}
data class TestOutcome(val lines:MutableList<String> = mutableListOf()) {
fun add(line:String) {
lines.add(line)
}
fun maxWidth(): Int {
return lines.maxBy { it.length }?.length ?: 0
}
}
@lestephane
Copy link

lestephane commented Apr 28, 2021

I think the line

ignoreFailures = true // Always try to run all tests for all modules

causes Gradle to end BUILD SUCCESSFUL (at least over here with Gradle 7), even with multiple test failures across multiple modules. This is not expected.

@lmj0011
Copy link

lmj0011 commented Nov 4, 2022

I appreciate you converting this to Kotlin DSL, I used your snippet as a reference to update this outdated stackoveflow answer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment