001 package org.junit.runners.model;
002
003 import java.lang.annotation.Annotation;
004 import java.lang.reflect.InvocationTargetException;
005 import java.lang.reflect.Method;
006 import java.lang.reflect.Type;
007 import java.util.List;
008
009 import org.junit.internal.runners.model.ReflectiveCallable;
010
011 /**
012 * Represents a method on a test class to be invoked at the appropriate point in
013 * test execution. These methods are usually marked with an annotation (such as
014 * {@code @Test}, {@code @Before}, {@code @After}, {@code @BeforeClass},
015 * {@code @AfterClass}, etc.)
016 *
017 * @since 4.5
018 */
019 public class FrameworkMethod extends FrameworkMember<FrameworkMethod> {
020 private final Method method;
021
022 /**
023 * Returns a new {@code FrameworkMethod} for {@code method}
024 */
025 public FrameworkMethod(Method method) {
026 if (method == null) {
027 throw new NullPointerException(
028 "FrameworkMethod cannot be created without an underlying method.");
029 }
030 this.method = method;
031
032 if (isPublic()) {
033 // This method could be a public method in a package-scope base class
034 try {
035 method.setAccessible(true);
036 } catch (SecurityException e) {
037 // We may get an IllegalAccessException when we try to call the method
038 }
039 }
040 }
041
042 /**
043 * Returns the underlying Java method
044 */
045 public Method getMethod() {
046 return method;
047 }
048
049 /**
050 * Returns the result of invoking this method on {@code target} with
051 * parameters {@code params}. {@link InvocationTargetException}s thrown are
052 * unwrapped, and their causes rethrown.
053 */
054 public Object invokeExplosively(final Object target, final Object... params)
055 throws Throwable {
056 return new ReflectiveCallable() {
057 @Override
058 protected Object runReflectiveCall() throws Throwable {
059 return method.invoke(target, params);
060 }
061 }.run();
062 }
063
064 /**
065 * Returns the method's name
066 */
067 @Override
068 public String getName() {
069 return method.getName();
070 }
071
072 /**
073 * Adds to {@code errors} if this method:
074 * <ul>
075 * <li>is not public, or
076 * <li>takes parameters, or
077 * <li>returns something other than void, or
078 * <li>is static (given {@code isStatic is false}), or
079 * <li>is not static (given {@code isStatic is true}).
080 * </ul>
081 */
082 public void validatePublicVoidNoArg(boolean isStatic, List<Throwable> errors) {
083 validatePublicVoid(isStatic, errors);
084 if (method.getParameterTypes().length != 0) {
085 errors.add(new Exception("Method " + method.getName() + " should have no parameters"));
086 }
087 }
088
089
090 /**
091 * Adds to {@code errors} if this method:
092 * <ul>
093 * <li>is not public, or
094 * <li>returns something other than void, or
095 * <li>is static (given {@code isStatic is false}), or
096 * <li>is not static (given {@code isStatic is true}).
097 * </ul>
098 */
099 public void validatePublicVoid(boolean isStatic, List<Throwable> errors) {
100 if (isStatic() != isStatic) {
101 String state = isStatic ? "should" : "should not";
102 errors.add(new Exception("Method " + method.getName() + "() " + state + " be static"));
103 }
104 if (!isPublic()) {
105 errors.add(new Exception("Method " + method.getName() + "() should be public"));
106 }
107 if (method.getReturnType() != Void.TYPE) {
108 errors.add(new Exception("Method " + method.getName() + "() should be void"));
109 }
110 }
111
112 @Override
113 protected int getModifiers() {
114 return method.getModifiers();
115 }
116
117 /**
118 * Returns the return type of the method
119 */
120 public Class<?> getReturnType() {
121 return method.getReturnType();
122 }
123
124 /**
125 * Returns the return type of the method
126 */
127 @Override
128 public Class<?> getType() {
129 return getReturnType();
130 }
131
132 /**
133 * Returns the class where the method is actually declared
134 */
135 @Override
136 public Class<?> getDeclaringClass() {
137 return method.getDeclaringClass();
138 }
139
140 public void validateNoTypeParametersOnArgs(List<Throwable> errors) {
141 new NoGenericTypeParametersValidator(method).validate(errors);
142 }
143
144 @Override
145 public boolean isShadowedBy(FrameworkMethod other) {
146 if (!other.getName().equals(getName())) {
147 return false;
148 }
149 if (other.getParameterTypes().length != getParameterTypes().length) {
150 return false;
151 }
152 for (int i = 0; i < other.getParameterTypes().length; i++) {
153 if (!other.getParameterTypes()[i].equals(getParameterTypes()[i])) {
154 return false;
155 }
156 }
157 return true;
158 }
159
160 @Override
161 boolean isBridgeMethod() {
162 return method.isBridge();
163 }
164
165 @Override
166 public boolean equals(Object obj) {
167 if (!FrameworkMethod.class.isInstance(obj)) {
168 return false;
169 }
170 return ((FrameworkMethod) obj).method.equals(method);
171 }
172
173 @Override
174 public int hashCode() {
175 return method.hashCode();
176 }
177
178 /**
179 * Returns true if this is a no-arg method that returns a value assignable
180 * to {@code type}
181 *
182 * @deprecated This is used only by the Theories runner, and does not
183 * use all the generic type info that it ought to. It will be replaced
184 * with a forthcoming ParameterSignature#canAcceptResultOf(FrameworkMethod)
185 * once Theories moves to junit-contrib.
186 */
187 @Deprecated
188 public boolean producesType(Type type) {
189 return getParameterTypes().length == 0 && type instanceof Class<?>
190 && ((Class<?>) type).isAssignableFrom(method.getReturnType());
191 }
192
193 private Class<?>[] getParameterTypes() {
194 return method.getParameterTypes();
195 }
196
197 /**
198 * Returns the annotations on this method
199 */
200 public Annotation[] getAnnotations() {
201 return method.getAnnotations();
202 }
203
204 /**
205 * Returns the annotation of type {@code annotationType} on this method, if
206 * one exists.
207 */
208 public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
209 return method.getAnnotation(annotationType);
210 }
211
212 @Override
213 public String toString() {
214 return method.toString();
215 }
216 }