package org.neo4j.graphalgo.competition;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.ParentRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.TestClass;
public class PhaseRunner extends ParentRunner<Runner>
{
@Target( ElementType.METHOD )
@Retention( RetentionPolicy.RUNTIME )
public @interface Phase
{
int value() default 0;
}
private static final Comparator<FrameworkMethod> PHASE_COMPARATOR = new Comparator<FrameworkMethod>()
{
public int compare( FrameworkMethod o1, FrameworkMethod o2 )
{
Class<?> c1 = o1.getMethod().getDeclaringClass();
Class<?> c2 = o2.getMethod().getDeclaringClass();
if ( c1 == c2 )
{
return o1.getAnnotation( Phase.class ).value()
- o2.getAnnotation( Phase.class ).value();
}
else if ( c1.isAssignableFrom( c2 ) )
{
return -1;
}
else
{
return 1;
}
}
};
private List<Runner> runners;
public PhaseRunner( Class<?> testClass ) throws InitializationError
{
super( testClass );
}
private static class InstanceRunner extends BlockJUnit4ClassRunner
{
private final Object instance;
private final String name;
InstanceRunner( Object instance, String name )
throws InitializationError
{
super( instance.getClass() );
this.instance = instance;
this.name = name;
}
@Override
protected void validateConstructor( List<Throwable> errors )
{
}
@Override
protected Object createTest() throws Exception
{
return instance;
}
@Override
protected Description describeChild( FrameworkMethod method )
{
Description child = super.describeChild( method );
return Description.createTestDescription( child.getTestClass(),
name + ":" + child.getMethodName() );
}
}
@Override
protected void collectInitializationErrors( List<Throwable> errors )
{
super.collectInitializationErrors( errors );
TestClass testclass = this.getTestClass();
Object target = null;
try
{
target = testclass.getOnlyConstructor().newInstance();
}
catch ( InvocationTargetException e )
{
errors.add( e.getTargetException() );
}
catch ( Throwable e )
{
errors.add( e );
}
List<FrameworkMethod> methods = testclass.getAnnotatedMethods( Phase.class );
Collections.sort( methods, PHASE_COMPARATOR );
runners = new LinkedList<Runner>();
for ( FrameworkMethod method : methods )
{
validatePublicObjectNoArg( method.getMethod(), errors );
if ( target != null )
{
Object instance;
try
{
instance = method.invokeExplosively( target );
}
catch ( Throwable e )
{
errors.add( e );
continue;
}
try
{
runners.add( new InstanceRunner( instance,
testclass.getJavaClass().getSimpleName() + "/"
+ method.getName() ) );
}
catch ( InitializationError e )
{
for ( Throwable t : e.getCauses() )
{
t.printStackTrace();
}
errors.addAll( e.getCauses() );
}
}
}
}
private void validatePublicObjectNoArg( Method fMethod,
List<Throwable> errors )
{
if ( !Modifier.isPublic( fMethod.getDeclaringClass().getModifiers() ) )
errors.add( new Exception( "Class "
+ fMethod.getDeclaringClass().getName()
+ " should be public" ) );
if ( !Modifier.isPublic( fMethod.getModifiers() ) )
errors.add( new Exception( "Method " + fMethod.getName()
+ "() should be public" ) );
if ( fMethod.getReturnType() == Void.TYPE )
errors.add( new Exception( "Method " + fMethod.getName()
+ "() should not be void" ) );
if ( fMethod.getParameterTypes().length != 0 )
errors.add( new Exception( "Method " + fMethod.getName()
+ " should have no parameters" ) );
}
@Override
protected Description describeChild( Runner child )
{
return child.getDescription();
}
@Override
protected List<Runner> getChildren()
{
return Collections.unmodifiableList( runners );
}
@Override
protected void runChild( Runner child, RunNotifier notifier )
{
child.run( notifier );
}
}