Build: Improve test output on errors and when debugging
There were a number of subtle issues with the existing logging that wraps events from Junit4 and ant. This change: * Tweaks at what level certain events are logged * Fixes -Dtests.output=always to force everything to be logged * Makes -Dtests.class imply -Dtests.output=always * Captures ant logging from junit4, so that direct jvm output will be logged on failure when not using gradle info logging
This commit is contained in:
parent
0d349854d3
commit
c273327246
|
@ -3,6 +3,9 @@ package com.carrotsearch.gradle.junit4
|
||||||
import com.carrotsearch.ant.tasks.junit4.ListenersList
|
import com.carrotsearch.ant.tasks.junit4.ListenersList
|
||||||
import com.carrotsearch.ant.tasks.junit4.listeners.AggregatedEventListener
|
import com.carrotsearch.ant.tasks.junit4.listeners.AggregatedEventListener
|
||||||
import groovy.xml.NamespaceBuilder
|
import groovy.xml.NamespaceBuilder
|
||||||
|
import groovy.xml.NamespaceBuilderSupport
|
||||||
|
import org.apache.tools.ant.BuildException
|
||||||
|
import org.apache.tools.ant.DefaultLogger
|
||||||
import org.apache.tools.ant.RuntimeConfigurable
|
import org.apache.tools.ant.RuntimeConfigurable
|
||||||
import org.apache.tools.ant.UnknownElement
|
import org.apache.tools.ant.UnknownElement
|
||||||
import org.gradle.api.DefaultTask
|
import org.gradle.api.DefaultTask
|
||||||
|
@ -180,11 +183,36 @@ class RandomizedTestingTask extends DefaultTask {
|
||||||
heartbeat: testLoggingConfig.slowTests.heartbeat,
|
heartbeat: testLoggingConfig.slowTests.heartbeat,
|
||||||
dir: workingDir,
|
dir: workingDir,
|
||||||
tempdir: new File(workingDir, 'temp'),
|
tempdir: new File(workingDir, 'temp'),
|
||||||
haltOnFailure: haltOnFailure,
|
haltOnFailure: true, // we want to capture when a build failed, but will decide whether to rethrow later
|
||||||
shuffleOnSlave: shuffleOnSlave
|
shuffleOnSlave: shuffleOnSlave
|
||||||
]
|
]
|
||||||
|
|
||||||
def junit4 = NamespaceBuilder.newInstance(ant, 'junit4')
|
DefaultLogger listener = null
|
||||||
|
ByteArrayOutputStream antLoggingBuffer = null
|
||||||
|
if (logger.isInfoEnabled() == false) {
|
||||||
|
// in info logging, ant already outputs info level, so we see everything
|
||||||
|
// but on errors or when debugging, we want to see info level messages
|
||||||
|
// because junit4 emits jvm output with ant logging
|
||||||
|
if (testLoggingConfig.outputMode == TestLoggingConfiguration.OutputMode.ALWAYS) {
|
||||||
|
// we want all output, so just stream directly
|
||||||
|
listener = new DefaultLogger(
|
||||||
|
errorPrintStream: System.err,
|
||||||
|
outputPrintStream: System.out,
|
||||||
|
messageOutputLevel: org.apache.tools.ant.Project.MSG_INFO)
|
||||||
|
} else {
|
||||||
|
// we want to buffer the info, and emit it if the test fails
|
||||||
|
antLoggingBuffer = new ByteArrayOutputStream()
|
||||||
|
PrintStream stream = new PrintStream(antLoggingBuffer, true, "UTF-8")
|
||||||
|
listener = new DefaultLogger(
|
||||||
|
errorPrintStream: stream,
|
||||||
|
outputPrintStream: stream,
|
||||||
|
messageOutputLevel: org.apache.tools.ant.Project.MSG_INFO)
|
||||||
|
}
|
||||||
|
project.ant.project.addBuildListener(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
NamespaceBuilderSupport junit4 = NamespaceBuilder.newInstance(ant, 'junit4')
|
||||||
|
try {
|
||||||
junit4.junit4(attributes) {
|
junit4.junit4(attributes) {
|
||||||
classpath {
|
classpath {
|
||||||
pathElement(path: classpath.asPath)
|
pathElement(path: classpath.asPath)
|
||||||
|
@ -214,6 +242,20 @@ class RandomizedTestingTask extends DefaultTask {
|
||||||
}
|
}
|
||||||
makeListeners()
|
makeListeners()
|
||||||
}
|
}
|
||||||
|
} catch (BuildException e) {
|
||||||
|
if (antLoggingBuffer != null) {
|
||||||
|
logger.error('JUnit4 test failed, ant output was:')
|
||||||
|
logger.error(antLoggingBuffer.toString('UTF-8'))
|
||||||
|
}
|
||||||
|
if (haltOnFailure) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listener != null) {
|
||||||
|
// remove the listener we added so other ant tasks dont have verbose logging!
|
||||||
|
project.ant.project.removeBuildListener(listener)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ListenersElement extends UnknownElement {
|
static class ListenersElement extends UnknownElement {
|
||||||
|
|
|
@ -79,7 +79,7 @@ class TestReportLogger extends TestsSummaryEventListener implements AggregatedEv
|
||||||
forkedJvmCount = e.getSlaveCount();
|
forkedJvmCount = e.getSlaveCount();
|
||||||
jvmIdFormat = " J%-" + (1 + (int) Math.floor(Math.log10(forkedJvmCount))) + "d";
|
jvmIdFormat = " J%-" + (1 + (int) Math.floor(Math.log10(forkedJvmCount))) + "d";
|
||||||
|
|
||||||
outStream = new LoggingOutputStream(logger: logger, level: LogLevel.ERROR, prefix: " 1> ")
|
outStream = new LoggingOutputStream(logger: logger, level: LogLevel.LIFECYCLE, prefix: " 1> ")
|
||||||
errStream = new LoggingOutputStream(logger: logger, level: LogLevel.ERROR, prefix: " 2> ")
|
errStream = new LoggingOutputStream(logger: logger, level: LogLevel.ERROR, prefix: " 2> ")
|
||||||
|
|
||||||
for (String contains : config.stackTraceFilters.contains) {
|
for (String contains : config.stackTraceFilters.contains) {
|
||||||
|
@ -152,13 +152,13 @@ class TestReportLogger extends TestsSummaryEventListener implements AggregatedEv
|
||||||
void onSuiteStart(AggregatedSuiteStartedEvent e) throws IOException {
|
void onSuiteStart(AggregatedSuiteStartedEvent e) throws IOException {
|
||||||
if (isPassthrough()) {
|
if (isPassthrough()) {
|
||||||
SuiteStartedEvent evt = e.getSuiteStartedEvent();
|
SuiteStartedEvent evt = e.getSuiteStartedEvent();
|
||||||
emitSuiteStart(LogLevel.INFO, evt.getDescription());
|
emitSuiteStart(LogLevel.LIFECYCLE, evt.getDescription());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
void onOutput(PartialOutputEvent e) throws IOException {
|
void onOutput(PartialOutputEvent e) throws IOException {
|
||||||
if (isPassthrough() && logger.isInfoEnabled()) {
|
if (isPassthrough()) {
|
||||||
// We only allow passthrough output if there is one JVM.
|
// We only allow passthrough output if there is one JVM.
|
||||||
switch (e.getEvent().getType()) {
|
switch (e.getEvent().getType()) {
|
||||||
case EventType.APPEND_STDERR:
|
case EventType.APPEND_STDERR:
|
||||||
|
@ -187,7 +187,6 @@ class TestReportLogger extends TestsSummaryEventListener implements AggregatedEv
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
void onSuiteResult(AggregatedSuiteResultEvent e) throws IOException {
|
void onSuiteResult(AggregatedSuiteResultEvent e) throws IOException {
|
||||||
try {
|
|
||||||
final int completed = suitesCompleted.incrementAndGet();
|
final int completed = suitesCompleted.incrementAndGet();
|
||||||
|
|
||||||
if (e.isSuccessful() && e.getTests().isEmpty()) {
|
if (e.isSuccessful() && e.getTests().isEmpty()) {
|
||||||
|
@ -197,7 +196,8 @@ class TestReportLogger extends TestsSummaryEventListener implements AggregatedEv
|
||||||
suiteTimes.put(e.getDescription().getDisplayName(), e.getExecutionTime())
|
suiteTimes.put(e.getDescription().getDisplayName(), e.getExecutionTime())
|
||||||
}
|
}
|
||||||
|
|
||||||
LogLevel level = e.isSuccessful() ? LogLevel.INFO : LogLevel.ERROR
|
LogLevel level = e.isSuccessful() && config.outputMode != OutputMode.ALWAYS ? LogLevel.INFO : LogLevel.LIFECYCLE
|
||||||
|
|
||||||
// We must emit buffered test and stream events (in case of failures).
|
// We must emit buffered test and stream events (in case of failures).
|
||||||
if (!isPassthrough()) {
|
if (!isPassthrough()) {
|
||||||
emitSuiteStart(level, e.getDescription())
|
emitSuiteStart(level, e.getDescription())
|
||||||
|
@ -214,9 +214,6 @@ class TestReportLogger extends TestsSummaryEventListener implements AggregatedEv
|
||||||
}
|
}
|
||||||
|
|
||||||
emitSuiteEnd(level, e, completed)
|
emitSuiteEnd(level, e, completed)
|
||||||
} catch (Exception exc) {
|
|
||||||
logger.lifecycle('EXCEPTION: ', exc)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Suite prologue. */
|
/** Suite prologue. */
|
||||||
|
@ -348,9 +345,9 @@ class TestReportLogger extends TestsSummaryEventListener implements AggregatedEv
|
||||||
errStream.flush()
|
errStream.flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns true if output should be logged immediately. Only relevant when running with INFO log level. */
|
/** Returns true if output should be logged immediately. */
|
||||||
boolean isPassthrough() {
|
boolean isPassthrough() {
|
||||||
return forkedJvmCount == 1 && config.outputMode == OutputMode.ALWAYS && logger.isInfoEnabled()
|
return forkedJvmCount == 1 && config.outputMode == OutputMode.ALWAYS
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -307,8 +307,13 @@ class BuildPlugin implements Plugin<Project> {
|
||||||
regex(/^(\s+at )(org\.apache\.lucene\.util\.TestRule)/)
|
regex(/^(\s+at )(org\.apache\.lucene\.util\.TestRule)/)
|
||||||
regex(/^(\s+at )(org\.apache\.lucene\.util\.AbstractBeforeAfterRule)/)
|
regex(/^(\s+at )(org\.apache\.lucene\.util\.AbstractBeforeAfterRule)/)
|
||||||
}
|
}
|
||||||
|
if (System.getProperty('tests.class') != null && System.getProperty('tests.output') == null) {
|
||||||
|
// if you are debugging, you want to see the output!
|
||||||
|
outputMode 'always'
|
||||||
|
} else {
|
||||||
outputMode System.getProperty('tests.output', 'onerror')
|
outputMode System.getProperty('tests.output', 'onerror')
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
balancers {
|
balancers {
|
||||||
executionTime cacheFilename: ".local-${project.version}-${name}-execution-times.log"
|
executionTime cacheFilename: ".local-${project.version}-${name}-execution-times.log"
|
||||||
|
|
|
@ -20,10 +20,9 @@ package org.elasticsearch.gradle.vagrant
|
||||||
|
|
||||||
import com.carrotsearch.gradle.junit4.LoggingOutputStream
|
import com.carrotsearch.gradle.junit4.LoggingOutputStream
|
||||||
import org.gradle.api.GradleScriptException
|
import org.gradle.api.GradleScriptException
|
||||||
import org.gradle.api.InvalidUserDataException
|
|
||||||
import org.gradle.api.logging.Logger
|
import org.gradle.api.logging.Logger
|
||||||
import org.gradle.logging.ProgressLogger
|
import org.gradle.logging.ProgressLogger
|
||||||
import org.gradle.logging.ProgressLoggerFactory
|
|
||||||
import java.util.regex.Matcher
|
import java.util.regex.Matcher
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -20,7 +20,6 @@ package org.elasticsearch.gradle.vagrant
|
||||||
|
|
||||||
import com.carrotsearch.gradle.junit4.LoggingOutputStream
|
import com.carrotsearch.gradle.junit4.LoggingOutputStream
|
||||||
import org.gradle.logging.ProgressLogger
|
import org.gradle.logging.ProgressLogger
|
||||||
import org.gradle.logging.ProgressLoggerFactory
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapts an OutputStream being written to by vagrant into a ProcessLogger. It
|
* Adapts an OutputStream being written to by vagrant into a ProcessLogger. It
|
||||||
|
|
Loading…
Reference in New Issue