001/* 002 * (C) Copyright 2014-2015 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Stephane Lacoin, Julien Carsique 016 * 017 */ 018package org.nuxeo.runtime.test.runner; 019 020import java.lang.annotation.ElementType; 021import java.lang.annotation.Retention; 022import java.lang.annotation.RetentionPolicy; 023import java.lang.annotation.Target; 024import java.lang.reflect.Field; 025import java.lang.reflect.Method; 026 027import javax.inject.Inject; 028import javax.inject.Named; 029 030import org.apache.commons.lang3.SystemUtils; 031import org.junit.ClassRule; 032import org.junit.Rule; 033import org.junit.rules.MethodRule; 034import org.junit.rules.TestRule; 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, TestRule { 041 @Inject 042 private RunNotifier runNotifier; 043 044 public static class Feature extends SimpleFeature { 045 protected static final ConditionalIgnoreRule rule = new ConditionalIgnoreRule(); 046 047 @ClassRule 048 public static TestRule classRule() { 049 return rule; 050 } 051 052 @Rule 053 public MethodRule methodRule() { 054 return rule; 055 } 056 } 057 058 @Retention(RetentionPolicy.RUNTIME) 059 @Target({ ElementType.TYPE, ElementType.METHOD }) 060 public @interface Ignore { 061 Class<? extends Condition> condition(); 062 063 /** 064 * Optional reason why the test is ignored, reported additionally to the condition class simple name. 065 */ 066 String cause() default ""; 067 } 068 069 public interface Condition { 070 boolean shouldIgnore(); 071 } 072 073 public static final class NXP10926H2Upgrade implements Condition { 074 @Override 075 public boolean shouldIgnore() { 076 return false; 077 } 078 } 079 080 public static final class IgnoreIsolated implements Condition { 081 boolean isIsolated = "org.nuxeo.runtime.testsuite.IsolatedClassloader".equals(getClass().getClassLoader() 082 .getClass() 083 .getName()); 084 085 @Override 086 public boolean shouldIgnore() { 087 return isIsolated; 088 } 089 } 090 091 public static final class IgnoreLongRunning implements Condition { 092 @Override 093 public boolean shouldIgnore() { 094 return true; 095 } 096 } 097 098 public static final class IgnoreWindows implements Condition { 099 @Override 100 public boolean shouldIgnore() { 101 return SystemUtils.IS_OS_WINDOWS; 102 } 103 } 104 105 @Override 106 public Statement apply(Statement base, FrameworkMethod method, Object fixtureTarget) { 107 Class<?> fixtureType = fixtureTarget.getClass(); 108 Method fixtureMethod = method.getMethod(); 109 Description description = Description.createTestDescription(fixtureType, fixtureMethod.getName(), 110 fixtureMethod.getAnnotations()); 111 if (fixtureType.isAnnotationPresent(Ignore.class)) { 112 return shouldIgnore(base, description, fixtureType.getAnnotation(Ignore.class), fixtureType, fixtureMethod, 113 fixtureTarget); 114 } 115 if (fixtureMethod.isAnnotationPresent(Ignore.class)) { 116 return shouldIgnore(base, description, fixtureMethod.getAnnotation(Ignore.class), fixtureType, 117 fixtureMethod, fixtureTarget); 118 } 119 return base; 120 } 121 122 @Override 123 public Statement apply(Statement base, Description description) { 124 Class<?> fixtureType = description.getTestClass(); 125 if (fixtureType.isAnnotationPresent(Ignore.class)) { 126 return shouldIgnore(base, description, fixtureType.getAnnotation(Ignore.class), fixtureType); 127 } 128 return base; 129 } 130 131 protected Statement shouldIgnore(Statement base, Description description, Ignore ignore, Class<?> type) { 132 return shouldIgnore(base, description, ignore, type, null, null); 133 } 134 135 protected Statement shouldIgnore(Statement base, Description description, Ignore ignore, Class<?> type, 136 Method method, Object target) { 137 Class<? extends Condition> conditionType = ignore.condition(); 138 if (conditionType == null) { 139 return base; 140 } 141 Condition condition = newCondition(type, method, target, conditionType); 142 if (!condition.shouldIgnore()) { 143 return base; 144 } 145 return new Statement() { 146 @Override 147 public void evaluate() throws Throwable { 148 runNotifier.fireTestIgnored(description); 149 } 150 }; 151 } 152 153 protected Condition newCondition(Class<?> type, Method method, Object target, 154 Class<? extends Condition> conditionType) throws Error { 155 Condition condition; 156 try { 157 condition = conditionType.newInstance(); 158 } catch (InstantiationException | IllegalAccessException cause) { 159 throw new Error("Cannot instantiate condition of type " + conditionType, cause); 160 } 161 injectCondition(type, method, target, condition); 162 return condition; 163 } 164 165 protected void injectCondition(Class<?> type, Method method, Object target, Condition condition) 166 throws SecurityException, Error { 167 Error errors = new Error("Cannot inject condition parameters in " + condition.getClass()); 168 for (Field eachField : condition.getClass().getDeclaredFields()) { 169 if (!eachField.isAnnotationPresent(Inject.class)) { 170 continue; 171 } 172 Object eachValue = null; 173 if (eachField.isAnnotationPresent(Named.class)) { 174 String name = eachField.getAnnotation(Named.class).value(); 175 if ("type".equals(name)) { 176 eachValue = type; 177 } else if ("target".equals(name)) { 178 eachValue = target; 179 } else if ("method".equals(name)) { 180 eachValue = method; 181 } 182 } else { 183 Class<?> eachType = eachField.getType(); 184 if (eachType.equals(Class.class)) { 185 eachValue = type; 186 } else if (eachType.equals(Object.class)) { 187 eachValue = target; 188 } else if (eachType.equals(Method.class)) { 189 eachValue = method; 190 } 191 } 192 if (eachValue == null) { 193 errors.addSuppressed(new Error("Cannot inject " + eachField.getName())); 194 } 195 eachField.setAccessible(true); 196 try { 197 eachField.set(condition, eachValue); 198 } catch (IllegalArgumentException | IllegalAccessException cause) { 199 errors.addSuppressed(new Error("Cannot inject " + eachField.getName(), cause)); 200 } 201 } 202 if (errors.getSuppressed().length > 0) { 203 throw errors; 204 } 205 } 206 207}