/******************************************************************************* * Copyright (c) 2013,2014 RĂ¼diger Herrmann * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * RĂ¼diger Herrmann - initial API and implementation * Matt Morrissette - allow to use non-static inner IgnoreConditions ******************************************************************************/ package com.codeaffine.test; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Modifier; import org.junit.Assume; import org.junit.rules.MethodRule; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.Statement; public class ConditionalIgnoreRule implements MethodRule { public interface IgnoreCondition { boolean isSatisfied(); } @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface ConditionalIgnore { Class<? extends IgnoreCondition> condition(); } @Override public Statement apply( Statement base, FrameworkMethod method, Object target ) { Statement result = base; if( hasConditionalIgnoreAnnotation( method ) ) { IgnoreCondition condition = getIgnoreContition( target, method ); if( condition.isSatisfied() ) { result = new IgnoreStatement( condition ); } } return result; } private static boolean hasConditionalIgnoreAnnotation( FrameworkMethod method ) { return method.getAnnotation( ConditionalIgnore.class ) != null; } private static IgnoreCondition getIgnoreContition( Object target, FrameworkMethod method ) { ConditionalIgnore annotation = method.getAnnotation( ConditionalIgnore.class ); return new IgnoreConditionCreator( target, annotation ).create(); } private static class IgnoreConditionCreator { private final Object target; private final Class<? extends IgnoreCondition> conditionType; IgnoreConditionCreator( Object target, ConditionalIgnore annotation ) { this.target = target; this.conditionType = annotation.condition(); } IgnoreCondition create() { checkConditionType(); try { return createCondition(); } catch( RuntimeException re ) { throw re; } catch( Exception e ) { throw new RuntimeException( e ); } } private IgnoreCondition createCondition() throws Exception { IgnoreCondition result; if( isConditionTypeStandalone() ) { result = conditionType.newInstance(); } else { result = conditionType.getDeclaredConstructor( target.getClass() ).newInstance( target ); } return result; } private void checkConditionType() { if( !isConditionTypeStandalone() && !isConditionTypeDeclaredInTarget() ) { String msg = "Conditional class '%s' is a member class " + "but was not declared inside the test case using it.\n" + "Either make this class a static class, " + "standalone class (by declaring it in it's own file) " + "or move it inside the test case using it"; throw new IllegalArgumentException( String.format ( msg, conditionType.getName() ) ); } } private boolean isConditionTypeStandalone() { return !conditionType.isMemberClass() || Modifier.isStatic( conditionType.getModifiers() ); } private boolean isConditionTypeDeclaredInTarget() { return target.getClass().isAssignableFrom( conditionType.getDeclaringClass() ); } } private static class IgnoreStatement extends Statement { private final IgnoreCondition condition; IgnoreStatement( IgnoreCondition condition ) { this.condition = condition; } @Override public void evaluate() { Assume.assumeTrue( "Ignored by " + condition.getClass().getSimpleName(), false ); } } }