/*******************************************************************************
* Copyright (c) 2016 Frank Becker and others.
* 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:
* Frank Becker and others - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.commons.sdk.util;
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.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import com.google.common.base.Throwables;
/**
* Cobbled together from: http://www.codeaffine.com/2013/11/18/a-junit-rule-to-conditionally-ignore-tests/
* https://gist.github.com/yinzara/9980184 http://cwd.dhemery.com/2010/12/junit-rules/
* (http://stackoverflow.com/questions/28145735/androidjunit4-class-org-junit-assume-assumetrue-assumptionviolatedexception/
*/
public class ConditionalIgnoreRule implements TestRule {
private final IFixtureJUnitClass fixtureJUnitClass;
public ConditionalIgnoreRule(IFixtureJUnitClass fixtureJUnitClass) {
super();
this.fixtureJUnitClass = fixtureJUnitClass;
}
public interface IgnoreCondition {
boolean isSatisfied(AbstractTestFixture fixture);
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface ConditionalIgnore {
Class<? extends IgnoreCondition> condition();
}
@Override
public Statement apply(Statement aStatement, Description aDescription) {
Statement result = aStatement;
if (hasConditionalIgnoreAnnotation(aDescription)) {
IgnoreCondition condition = getIgnoreCondition(aDescription);
if (condition.isSatisfied(fixtureJUnitClass.getActualFixture())) {
result = new IgnoreStatement();
}
}
return result;
}
private static boolean hasConditionalIgnoreAnnotation(Description aDescription) {
return aDescription.getAnnotation(ConditionalIgnore.class) != null;
}
private static IgnoreCondition getIgnoreCondition(Description aDescription) {
ConditionalIgnore annotation = aDescription.getAnnotation(ConditionalIgnore.class);
return new IgnoreConditionCreator(aDescription.getTestClass(), annotation).create();
}
private static class IgnoreConditionCreator {
private final Class<?> testClass;
private final Class<? extends IgnoreCondition> conditionType;
IgnoreConditionCreator(Class<?> testClass, ConditionalIgnore annotation) {
this.testClass = testClass;
this.conditionType = annotation.condition();
}
IgnoreCondition create() {
checkConditionType();
try {
return createCondition();
} catch (Exception re) {
throw Throwables.propagate(re);
}
}
private IgnoreCondition createCondition() throws Exception {
IgnoreCondition result;
if (isConditionTypeStandalone()) {
result = conditionType.newInstance();
} else {
result = conditionType.getDeclaredConstructor(testClass).newInstance(testClass);
}
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 testClass.getClass().isAssignableFrom(conditionType.getDeclaringClass());
}
}
}