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 {

Reply via email to