blob: ce8ad881711c21f599a658885dc2a26f540b4aca [file] [log] [blame]
package org.apache.maven.surefire.junitcore;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.util.Map;
import org.apache.maven.surefire.api.report.ConsoleOutputReceiver;
import org.apache.maven.surefire.api.report.ConsoleStream;
import org.apache.maven.surefire.api.report.ReportEntry;
import org.apache.maven.surefire.api.report.ReporterFactory;
import org.apache.maven.surefire.api.report.RunListener;
import org.apache.maven.surefire.api.report.RunMode;
import org.apache.maven.surefire.api.report.StackTraceWriter;
import org.apache.maven.surefire.api.report.TestSetReportEntry;
import org.apache.maven.surefire.api.testset.TestSetFailedException;
import static org.apache.maven.surefire.junitcore.TestMethod.getThreadTestMethod;
/**
* Handles responses from concurrent junit
* <br>
* Stuff to remember about JUnit threading:
* parallel=classes; beforeClass/afterClass, constructor and all tests method run on same thread
* parallel=methods; beforeClass/afterClass run on main thread, constructor + each test method run on same thread
* parallel=both; same as parallel=methods
*
* @see org.apache.maven.surefire.junitcore.JUnitCoreRunListener for details about regular junit run listening
* @author Kristian Rosenvold
*/
public abstract class ConcurrentRunListener
implements RunListener, ConsoleOutputReceiver
{
private final Map<String, TestSet> classMethodCounts;
private final ThreadLocal<RunListener> reporterManagerThreadLocal;
private final boolean reportImmediately;
private final ConsoleStream consoleStream;
ConcurrentRunListener( final ReporterFactory reporterFactory, ConsoleStream consoleStream,
boolean reportImmediately, Map<String, TestSet> classMethodCounts )
throws TestSetFailedException
{
this.reportImmediately = reportImmediately;
this.classMethodCounts = classMethodCounts;
this.consoleStream = consoleStream;
reporterManagerThreadLocal = new ThreadLocal<RunListener>()
{
@Override
protected RunListener initialValue()
{
return reporterFactory.createReporter();
}
};
}
@Override
public void testSetStarting( TestSetReportEntry description )
{
}
@Override
public void testSetCompleted( TestSetReportEntry result )
{
try
{
final RunListener reporterManager = getRunListener();
for ( TestSet testSet : classMethodCounts.values() )
{
testSet.replay( reporterManager );
}
}
finally
{
reporterManagerThreadLocal.remove();
}
}
@Override
public void testFailed( ReportEntry failure )
{
final TestMethod testMethod = getOrCreateThreadAttachedTestMethod( failure );
if ( testMethod != null )
{
testMethod.testFailure( failure );
testMethod.detachFromCurrentThread();
}
}
@Override
public void testError( ReportEntry failure )
{
final TestMethod testMethod = getOrCreateThreadAttachedTestMethod( failure );
if ( testMethod != null )
{
testMethod.testError( failure );
testMethod.detachFromCurrentThread();
}
}
@Override
public void testSkipped( ReportEntry description )
{
TestSet testSet = getTestSet( description );
TestMethod testMethod = testSet.createThreadAttachedTestMethod( description );
testMethod.testIgnored( description );
testSet.incrementFinishedTests( getRunListener(), reportImmediately );
testMethod.detachFromCurrentThread();
}
@Override
public void testExecutionSkippedByUser()
{
// cannot guarantee proper call to all listeners
getRunListener().testExecutionSkippedByUser();
}
public RunMode markAs( RunMode currentRunMode )
{
return reporterManagerThreadLocal.get().markAs( currentRunMode );
}
@Override
public void testAssumptionFailure( ReportEntry failure )
{
final TestMethod testMethod = getOrCreateThreadAttachedTestMethod( failure );
if ( testMethod != null )
{
testMethod.testAssumption( failure );
testMethod.detachFromCurrentThread();
}
}
@Override
public void testStarting( ReportEntry description )
{
TestSet testSet = getTestSet( description );
testSet.createThreadAttachedTestMethod( description );
checkIfTestSetCanBeReported( testSet );
testSet.attachToThread();
}
@Override
public void testSucceeded( ReportEntry report )
{
TestMethod testMethod = getThreadTestMethod();
if ( testMethod != null )
{
testMethod.testFinished();
testMethod.getTestSet().incrementFinishedTests( getRunListener(), reportImmediately );
testMethod.detachFromCurrentThread();
}
}
private TestMethod getOrCreateThreadAttachedTestMethod( ReportEntry description )
{
TestMethod threadTestMethod = getThreadTestMethod();
if ( threadTestMethod != null )
{
return threadTestMethod;
}
TestSet testSet = getTestSet( description );
if ( testSet == null )
{
consoleStream.println( description.getName() );
StackTraceWriter writer = description.getStackTraceWriter();
if ( writer != null )
{
consoleStream.println( writer.writeTraceToString() );
}
return null;
}
else
{
return testSet.createThreadAttachedTestMethod( description );
}
}
protected abstract void checkIfTestSetCanBeReported( TestSet testSetForTest );
private TestSet getTestSet( ReportEntry description )
{
return classMethodCounts.get( description.getSourceName() );
}
RunListener getRunListener()
{
return reporterManagerThreadLocal.get();
}
public static ConcurrentRunListener createInstance( Map<String, TestSet> classMethodCounts,
ReporterFactory reporterFactory,
boolean parallelClasses, boolean parallelBoth,
ConsoleStream consoleStream )
throws TestSetFailedException
{
return parallelClasses
? new ClassesParallelRunListener( classMethodCounts, reporterFactory, consoleStream )
: new MethodsParallelRunListener( classMethodCounts, reporterFactory, !parallelBoth, consoleStream );
}
@Override
public void writeTestOutput( String output, boolean newLine, boolean stdout )
{
TestMethod threadTestMethod = getThreadTestMethod();
if ( threadTestMethod != null )
{
LogicalStream logicalStream = threadTestMethod.getLogicalStream();
logicalStream.write( stdout, output, newLine );
}
else
{
// Not able to associate output with any thread. Just dump to console
consoleStream.println( output );
}
}
}