blob: 219e06406515e97ab0c02986e8f447f97d508776 [file] [log] [blame]
package org.apache.maven.surefire.junit;
/*
* 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.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import org.apache.maven.surefire.api.report.LegacyPojoStackTraceWriter;
import org.apache.maven.surefire.api.report.ReportEntry;
import org.apache.maven.surefire.api.report.RunListener;
import org.apache.maven.surefire.api.report.SimpleReportEntry;
import org.apache.maven.surefire.api.report.StackTraceWriter;
import static org.apache.maven.surefire.api.report.SimpleReportEntry.withException;
import static org.apache.maven.surefire.api.util.internal.TestClassMethodNameUtils.extractClassName;
import static org.apache.maven.surefire.api.util.internal.TestClassMethodNameUtils.extractMethodName;
/**
* Invocation Handler for TestListener proxies to delegate to our {@link RunListener}
*
*/
public class TestListenerInvocationHandler
implements InvocationHandler
{
// The String names of the four methods in interface junit.framework.TestListener
private static final String START_TEST = "startTest";
private static final String ADD_FAILURE = "addFailure";
private static final String ADD_ERROR = "addError";
private static final String END_TEST = "endTest";
private final Set<FailedTest> failedTestsSet = new HashSet<>();
private RunListener reporter;
private static final Class[] EMPTY_CLASS_ARRAY = { };
private static final Object[] EMPTY_STRING_ARRAY = { };
private static class FailedTest
{
private Object testThatFailed;
private Thread threadOnWhichTestFailed;
FailedTest( Object testThatFailed, Thread threadOnWhichTestFailed )
{
if ( testThatFailed == null )
{
throw new NullPointerException( "testThatFailed is null" );
}
if ( threadOnWhichTestFailed == null )
{
throw new NullPointerException( "threadOnWhichTestFailed is null" );
}
this.testThatFailed = testThatFailed;
this.threadOnWhichTestFailed = threadOnWhichTestFailed;
}
@Override
public boolean equals( Object obj )
{
boolean retVal = true;
if ( obj == null || getClass() != obj.getClass() )
{
retVal = false;
}
else
{
FailedTest ft = (FailedTest) obj;
if ( ft.testThatFailed != testThatFailed )
{
retVal = false;
}
else if ( !ft.threadOnWhichTestFailed.equals( threadOnWhichTestFailed ) )
{
retVal = false;
}
}
return retVal;
}
@Override
public int hashCode()
{
return threadOnWhichTestFailed.hashCode();
}
}
public TestListenerInvocationHandler( RunListener reporter )
{
if ( reporter == null )
{
throw new NullPointerException( "reporter is null" );
}
this.reporter = reporter;
}
@Override
public Object invoke( Object proxy, Method method, Object[] args )
throws Throwable
{
String methodName = method.getName();
switch ( methodName )
{
case START_TEST:
handleStartTest( args );
break;
case ADD_ERROR:
handleAddError( args );
break;
case ADD_FAILURE:
handleAddFailure( args );
break;
case END_TEST:
handleEndTest( args );
break;
default:
break;
}
return null;
}
// Handler for TestListener.startTest(Test)
private void handleStartTest( Object[] args )
{
ReportEntry report = createStartEndReportEntry( args );
reporter.testStarting( report );
}
// Handler for TestListener.addFailure(Test, Throwable)
private void handleAddError( Object[] args )
throws ReflectiveOperationException
{
ReportEntry report = toReportEntryWithException( args );
reporter.testError( report );
failedTestsSet.add( new FailedTest( args[0], Thread.currentThread() ) );
}
private static LegacyPojoStackTraceWriter toStackTraceWriter( Object[] args )
throws ReflectiveOperationException
{
String testName;
try
{
Method m = args[0].getClass().getMethod( "getName", EMPTY_CLASS_ARRAY );
testName = (String) m.invoke( args[0], EMPTY_STRING_ARRAY );
}
catch ( NoSuchMethodException e )
{
testName = "UNKNOWN";
}
return new LegacyPojoStackTraceWriter( args[0].getClass().getName(), testName, (Throwable) args[1] );
}
private void handleAddFailure( Object[] args )
throws ReflectiveOperationException
{
ReportEntry report = toReportEntryWithException( args );
reporter.testFailed( report );
failedTestsSet.add( new FailedTest( args[0], Thread.currentThread() ) );
}
private void handleEndTest( Object[] args )
{
boolean testHadFailed = failedTestsSet.remove( new FailedTest( args[0], Thread.currentThread() ) );
if ( !testHadFailed )
{
ReportEntry report = createStartEndReportEntry( args );
reporter.testSucceeded( report );
}
}
private static ReportEntry toReportEntryWithException( Object[] args )
throws ReflectiveOperationException
{
String description = args[0].toString();
String className = extractClassName( description );
String methodName = extractMethodName( description );
StackTraceWriter stackTraceWriter = toStackTraceWriter( args );
return withException( className, null, methodName, null, stackTraceWriter );
}
private static SimpleReportEntry createStartEndReportEntry( Object[] args )
{
String description = args[0].toString();
return new SimpleReportEntry( extractClassName( description ), null, extractMethodName( description ), null );
}
}