This is an automated email from the ASF dual-hosted git repository. ewencp pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/kafka.git
The following commit(s) were added to refs/heads/trunk by this push: new ed30712 MINOR: Save failed test output to build output directory ed30712 is described below commit ed3071231aee1ba8a5c2c496112dd6034f9bf942 Author: Ewen Cheslack-Postava <m...@ewencp.org> AuthorDate: Fri Feb 15 10:50:08 2019 -0800 MINOR: Save failed test output to build output directory Author: Ewen Cheslack-Postava <m...@ewencp.org> Reviewers: Colin Patrick McCabe <co...@cmccabe.xyz> Closes #6234 from ewencp/test-logs --- build.gradle | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 420edf7..ff316c8 100644 --- a/build.gradle +++ b/build.gradle @@ -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()) { } } + subprojects { apply plugin: 'java' // apply the eclipse plugin only to subprojects that hold code. 'connect' is just a folder. @@ -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 { showStandardStreams = userShowStandardStreams ?: testShowStandardStreams exceptionFormat = testExceptionFormat } - + logTestStdout.rehydrate(delegate, owner, this)() } task integrationTest(type: Test, dependsOn: compileJava) { @@ -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 { showStandardStreams = userShowStandardStreams ?: testShowStandardStreams exceptionFormat = testExceptionFormat } + logTestStdout.rehydrate(delegate, owner, this)() if (it.project.name != 'generator') { useJUnit {