001/* 002 * (C) Copyright 2014-2015 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Stephane Lacoin, Julien Carsique 018 * 019 */ 020package org.nuxeo.runtime.test.runner; 021 022import java.lang.annotation.ElementType; 023import java.lang.annotation.Retention; 024import java.lang.annotation.RetentionPolicy; 025import java.lang.annotation.Target; 026import java.lang.reflect.Field; 027import java.lang.reflect.Method; 028 029import javax.inject.Inject; 030import javax.inject.Named; 031 032import org.apache.commons.lang3.SystemUtils; 033import org.junit.Rule; 034import org.junit.rules.MethodRule; 035import org.junit.runner.Description; 036import org.junit.runner.notification.RunNotifier; 037import org.junit.runners.model.FrameworkMethod; 038import org.junit.runners.model.Statement; 039 040public class ConditionalIgnoreRule implements MethodRule { 041 @Inject 042 private RunNotifier runNotifier; 043 044 @Inject 045 private FeaturesRunner runner; 046 047 public static class Feature extends SimpleFeature { 048 protected static final ConditionalIgnoreRule rule = new ConditionalIgnoreRule(); 049 050 @Rule 051 public MethodRule methodRule() { 052 return rule; 053 } 054 055 } 056 057 @Retention(RetentionPolicy.RUNTIME) 058 @Target({ ElementType.TYPE, ElementType.METHOD }) 059 public @interface Ignore { 060 Class<? extends Condition> condition(); 061 062 /** 063 * Optional reason why the test is ignored, reported additionally to the condition class simple name. 064 */ 065 String cause() default ""; 066 } 067 068 public interface Condition { 069 boolean shouldIgnore(); 070 } 071 072 public static final class NXP10926H2Upgrade implements Condition { 073 @Override 074 public boolean shouldIgnore() { 075 return false; 076 } 077 } 078 079 public static final class IgnoreIsolated implements Condition { 080 boolean isIsolated = "org.nuxeo.runtime.testsuite.IsolatedClassloader" 081 .equals(getClass().getClassLoader().getClass().getName()); 082 083 @Override 084 public boolean shouldIgnore() { 085 return isIsolated; 086 } 087 } 088 089 public static final class IgnoreLongRunning implements Condition { 090 @Override 091 public boolean shouldIgnore() { 092 return true; 093 } 094 } 095 096 public static final class IgnoreWindows implements Condition { 097 @Override 098 public boolean shouldIgnore() { 099 return SystemUtils.IS_OS_WINDOWS; 100 } 101 } 102 103 @Override 104 public Statement apply(Statement base, FrameworkMethod method, Object fixtureTarget) { 105 Class<?> fixtureType = fixtureTarget.getClass(); 106 Method fixtureMethod = method.getMethod(); 107 Description description = Description.createTestDescription(fixtureType, fixtureMethod.getName(), 108 fixtureMethod.getAnnotations()); 109 return shouldIgnore(base, description, runner.getConfig(method, Ignore.class), fixtureType, fixtureMethod, 110 fixtureTarget); 111 } 112 113 protected Statement shouldIgnore(Statement base, Description description, Ignore ignore, Class<?> type) { 114 return shouldIgnore(base, description, ignore, type, null, null); 115 } 116 117 protected Statement shouldIgnore(Statement base, Description description, Ignore ignore, Class<?> type, 118 Method method, Object target) { 119 Class<? extends Condition> conditionType = ignore.condition(); 120 if (conditionType == null) { 121 return base; 122 } 123 if (!newCondition(type, method, target, conditionType).shouldIgnore()) { 124 return base; 125 } 126 return new Statement() { 127 128 @Override 129 public void evaluate() throws Throwable { 130 runNotifier.fireTestIgnored(description); 131 } 132 }; 133 } 134 135 protected Condition newCondition(Class<?> type, Method method, Object target, 136 Class<? extends Condition> conditionType) throws Error { 137 Condition condition; 138 try { 139 condition = conditionType.newInstance(); 140 } catch (InstantiationException | IllegalAccessException cause) { 141 throw new Error("Cannot instantiate condition of type " + conditionType, cause); 142 } 143 injectCondition(type, method, target, condition); 144 return condition; 145 } 146 147 protected void injectCondition(Class<?> type, Method method, Object target, Condition condition) 148 throws SecurityException, Error { 149 Error errors = new Error("Cannot inject condition parameters in " + condition.getClass()); 150 for (Field eachField : condition.getClass().getDeclaredFields()) { 151 if (!eachField.isAnnotationPresent(Inject.class)) { 152 continue; 153 } 154 Object eachValue = null; 155 if (eachField.isAnnotationPresent(Named.class)) { 156 String name = eachField.getAnnotation(Named.class).value(); 157 if ("type".equals(name)) { 158 eachValue = type; 159 } else if ("target".equals(name)) { 160 eachValue = target; 161 } else if ("method".equals(name)) { 162 eachValue = method; 163 } 164 } else { 165 Class<?> eachType = eachField.getType(); 166 if (eachType.equals(Class.class)) { 167 eachValue = type; 168 } else if (eachType.equals(Object.class)) { 169 eachValue = target; 170 } else if (eachType.equals(Method.class)) { 171 eachValue = method; 172 } 173 } 174 if (eachValue == null) { 175 continue; 176 } 177 eachField.setAccessible(true); 178 try { 179 eachField.set(condition, eachValue); 180 } catch (IllegalArgumentException | IllegalAccessException cause) { 181 errors.addSuppressed(new Error("Cannot inject " + eachField.getName(), cause)); 182 } 183 } 184 if (errors.getSuppressed().length > 0) { 185 throw errors; 186 } 187 runner.getInjector().injectMembers(condition); 188 } 189 190}