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.ClassRule; 034import org.junit.Rule; 035import org.junit.rules.MethodRule; 036import org.junit.rules.TestRule; 037import org.junit.runner.Description; 038import org.junit.runner.notification.RunNotifier; 039import org.junit.runners.model.FrameworkMethod; 040import org.junit.runners.model.Statement; 041 042public class ConditionalIgnoreRule implements MethodRule, TestRule { 043 @Inject 044 private RunNotifier runNotifier; 045 046 public static class Feature extends SimpleFeature { 047 protected static final ConditionalIgnoreRule rule = new ConditionalIgnoreRule(); 048 049 @ClassRule 050 public static TestRule classRule() { 051 return rule; 052 } 053 054 @Rule 055 public MethodRule methodRule() { 056 return rule; 057 } 058 } 059 060 @Retention(RetentionPolicy.RUNTIME) 061 @Target({ ElementType.TYPE, ElementType.METHOD }) 062 public @interface Ignore { 063 Class<? extends Condition> condition(); 064 065 /** 066 * Optional reason why the test is ignored, reported additionally to the condition class simple name. 067 */ 068 String cause() default ""; 069 } 070 071 public interface Condition { 072 boolean shouldIgnore(); 073 } 074 075 public static final class NXP10926H2Upgrade implements Condition { 076 @Override 077 public boolean shouldIgnore() { 078 return false; 079 } 080 } 081 082 public static final class IgnoreIsolated implements Condition { 083 boolean isIsolated = "org.nuxeo.runtime.testsuite.IsolatedClassloader".equals(getClass().getClassLoader() 084 .getClass() 085 .getName()); 086 087 @Override 088 public boolean shouldIgnore() { 089 return isIsolated; 090 } 091 } 092 093 public static final class IgnoreLongRunning implements Condition { 094 @Override 095 public boolean shouldIgnore() { 096 return true; 097 } 098 } 099 100 public static final class IgnoreWindows implements Condition { 101 @Override 102 public boolean shouldIgnore() { 103 return SystemUtils.IS_OS_WINDOWS; 104 } 105 } 106 107 @Override 108 public Statement apply(Statement base, FrameworkMethod method, Object fixtureTarget) { 109 Class<?> fixtureType = fixtureTarget.getClass(); 110 Method fixtureMethod = method.getMethod(); 111 Description description = Description.createTestDescription(fixtureType, fixtureMethod.getName(), 112 fixtureMethod.getAnnotations()); 113 if (fixtureType.isAnnotationPresent(Ignore.class)) { 114 return shouldIgnore(base, description, fixtureType.getAnnotation(Ignore.class), fixtureType, fixtureMethod, 115 fixtureTarget); 116 } 117 if (fixtureMethod.isAnnotationPresent(Ignore.class)) { 118 return shouldIgnore(base, description, fixtureMethod.getAnnotation(Ignore.class), fixtureType, 119 fixtureMethod, fixtureTarget); 120 } 121 return base; 122 } 123 124 @Override 125 public Statement apply(Statement base, Description description) { 126 Class<?> fixtureType = description.getTestClass(); 127 if (fixtureType.isAnnotationPresent(Ignore.class)) { 128 return shouldIgnore(base, description, fixtureType.getAnnotation(Ignore.class), fixtureType); 129 } 130 return base; 131 } 132 133 protected Statement shouldIgnore(Statement base, Description description, Ignore ignore, Class<?> type) { 134 return shouldIgnore(base, description, ignore, type, null, null); 135 } 136 137 protected Statement shouldIgnore(Statement base, Description description, Ignore ignore, Class<?> type, 138 Method method, Object target) { 139 Class<? extends Condition> conditionType = ignore.condition(); 140 if (conditionType == null) { 141 return base; 142 } 143 Condition condition = newCondition(type, method, target, conditionType); 144 if (!condition.shouldIgnore()) { 145 return base; 146 } 147 return new Statement() { 148 @Override 149 public void evaluate() throws Throwable { 150 runNotifier.fireTestIgnored(description); 151 } 152 }; 153 } 154 155 protected Condition newCondition(Class<?> type, Method method, Object target, 156 Class<? extends Condition> conditionType) throws Error { 157 Condition condition; 158 try { 159 condition = conditionType.newInstance(); 160 } catch (InstantiationException | IllegalAccessException cause) { 161 throw new Error("Cannot instantiate condition of type " + conditionType, cause); 162 } 163 injectCondition(type, method, target, condition); 164 return condition; 165 } 166 167 protected void injectCondition(Class<?> type, Method method, Object target, Condition condition) 168 throws SecurityException, Error { 169 Error errors = new Error("Cannot inject condition parameters in " + condition.getClass()); 170 for (Field eachField : condition.getClass().getDeclaredFields()) { 171 if (!eachField.isAnnotationPresent(Inject.class)) { 172 continue; 173 } 174 Object eachValue = null; 175 if (eachField.isAnnotationPresent(Named.class)) { 176 String name = eachField.getAnnotation(Named.class).value(); 177 if ("type".equals(name)) { 178 eachValue = type; 179 } else if ("target".equals(name)) { 180 eachValue = target; 181 } else if ("method".equals(name)) { 182 eachValue = method; 183 } 184 } else { 185 Class<?> eachType = eachField.getType(); 186 if (eachType.equals(Class.class)) { 187 eachValue = type; 188 } else if (eachType.equals(Object.class)) { 189 eachValue = target; 190 } else if (eachType.equals(Method.class)) { 191 eachValue = method; 192 } 193 } 194 if (eachValue == null) { 195 errors.addSuppressed(new Error("Cannot inject " + eachField.getName())); 196 } 197 eachField.setAccessible(true); 198 try { 199 eachField.set(condition, eachValue); 200 } catch (IllegalArgumentException | IllegalAccessException cause) { 201 errors.addSuppressed(new Error("Cannot inject " + eachField.getName(), cause)); 202 } 203 } 204 if (errors.getSuppressed().length > 0) { 205 throw errors; 206 } 207 } 208 209}