Browse Source

MINOR: Save failed test output to build output directory

Author: Ewen Cheslack-Postava <me@ewencp.org>

Reviewers: Colin Patrick McCabe <colin@cmccabe.xyz>

Closes #6234 from ewencp/test-logs
pull/6260/head
Ewen Cheslack-Postava 6 years ago
parent
commit
ed3071231a
  1. 66
      build.gradle

66
build.gradle

@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
import org.ajoberstar.grgit.Grgit
import java.nio.charset.StandardCharsets
buildscript {
repositories {
mavenCentral()
@ -139,6 +141,7 @@ if (file('.git').exists()) { @@ -139,6 +141,7 @@ if (file('.git').exists()) {
}
}
subprojects {
apply plugin: 'java'
// apply the eclipse plugin only to subprojects that hold code. 'connect' is just a folder.
@ -204,6 +207,65 @@ subprojects { @@ -204,6 +207,65 @@ subprojects {
def testLoggingEvents = ["passed", "skipped", "failed"]
def testShowStandardStreams = false
def testExceptionFormat = 'full'
// Gradle built-in logging only supports sending test output to stdout, which generates a lot
// of noise, especially for passing tests. We really only want output for failed tests. This
// hooks into the output and logs it (so we don't have to buffer it all in memory) and only
// saves the output for failing tests. Directory and filenames are such that you can, e.g.,
// create a Jenkins rule to collect failed test output.
def logTestStdout = {
def testId = { TestDescriptor descriptor ->
"${descriptor.className}.${descriptor.name}".toString()
}
def logFiles = new HashMap<String, File>()
def logStreams = new HashMap<String, FileOutputStream>()
beforeTest { TestDescriptor td ->
def tid = testId(td)
def logFile = new File(
"${projectDir}/build/reports/testOutput/${tid}.test.stdout")
logFile.parentFile.mkdirs()
logFiles.put(tid, logFile)
logStreams.put(tid, new FileOutputStream(logFile))
}
onOutput { TestDescriptor td, TestOutputEvent toe ->
def tid = testId(td)
// Some output can happen outside the context of a specific test (e.g. at the class level)
// and beforeTest/afterTest seems to not be invoked for these cases (and similarly, there's
// a TestDescriptor hierarchy that includes the thread executing the test, Gradle tasks,
// etc). We see some of these in practice and it seems like something buggy in the Gradle
// test runner since we see it *before* any tests and it is frequently not related to any
// code in the test (best guess is that it is tail output from last test). We won't have
// an output file for these, so simply ignore them. If they become critical for debugging,
// they can be seen with showStandardStreams.
if (td.name == td.className) {
return
}
try {
logStreams.get(tid).write(toe.message.getBytes(StandardCharsets.UTF_8))
} catch (Exception e) {
println "ERROR: Failed to write output for test ${tid}"
e.printStackTrace()
}
}
afterTest { TestDescriptor td, TestResult tr ->
def tid = testId(td)
try {
logStreams.get(tid).close()
if (tr.resultType != TestResult.ResultType.FAILURE) {
logFiles.get(tid).delete()
} else {
def file = logFiles.get(tid)
println "${tid} failed, log available in ${file}"
}
} catch (Exception e) {
println "ERROR: Failed to close stdout file for ${tid}"
e.printStackTrace()
} finally {
logFiles.remove(tid)
logStreams.remove(tid)
}
}
}
test {
maxParallelForks = userMaxForks ?: Runtime.runtime.availableProcessors()
@ -216,7 +278,7 @@ subprojects { @@ -216,7 +278,7 @@ subprojects {
showStandardStreams = userShowStandardStreams ?: testShowStandardStreams
exceptionFormat = testExceptionFormat
}
logTestStdout.rehydrate(delegate, owner, this)()
}
task integrationTest(type: Test, dependsOn: compileJava) {
@ -230,6 +292,7 @@ subprojects { @@ -230,6 +292,7 @@ subprojects {
showStandardStreams = userShowStandardStreams ?: testShowStandardStreams
exceptionFormat = testExceptionFormat
}
logTestStdout.rehydrate(delegate, owner, this)()
useJUnit {
includeCategories 'org.apache.kafka.test.IntegrationTest'
@ -248,6 +311,7 @@ subprojects { @@ -248,6 +311,7 @@ subprojects {
showStandardStreams = userShowStandardStreams ?: testShowStandardStreams
exceptionFormat = testExceptionFormat
}
logTestStdout.rehydrate(delegate, owner, this)()
if (it.project.name != 'generator') {
useJUnit {

Loading…
Cancel
Save