1 package org.junit.runners.model;
2 
3 import java.lang.annotation.Annotation;
4 import java.lang.reflect.InvocationTargetException;
5 import java.lang.reflect.Method;
6 import java.lang.reflect.Type;
7 import java.util.List;
8 
9 import org.junit.internal.runners.model.ReflectiveCallable;
10 
11 /**
12  * Represents a method on a test class to be invoked at the appropriate point in
13  * test execution. These methods are usually marked with an annotation (such as
14  * {@code @Test}, {@code @Before}, {@code @After}, {@code @BeforeClass},
15  * {@code @AfterClass}, etc.)
16  *
17  * @since 4.5
18  */
19 public class FrameworkMethod extends FrameworkMember<FrameworkMethod> {
20     private final Method method;
21 
22     /**
23      * Returns a new {@code FrameworkMethod} for {@code method}
24      */
FrameworkMethod(Method method)25     public FrameworkMethod(Method method) {
26         if (method == null) {
27             throw new NullPointerException(
28                     "FrameworkMethod cannot be created without an underlying method.");
29         }
30         this.method = method;
31 
32         if (isPublic()) {
33             // This method could be a public method in a package-scope base class
34             try {
35                 method.setAccessible(true);
36             } catch (SecurityException  e) {
37                 // We may get an IllegalAccessException when we try to call the method
38             }
39         }
40     }
41 
42     /**
43      * Returns the underlying Java method
44      */
getMethod()45     public Method getMethod() {
46         return method;
47     }
48 
49     /**
50      * Returns the result of invoking this method on {@code target} with
51      * parameters {@code params}. {@link InvocationTargetException}s thrown are
52      * unwrapped, and their causes rethrown.
53      */
invokeExplosively(final Object target, final Object... params)54     public Object invokeExplosively(final Object target, final Object... params)
55             throws Throwable {
56         return new ReflectiveCallable() {
57             @Override
58             protected Object runReflectiveCall() throws Throwable {
59                 return method.invoke(target, params);
60             }
61         }.run();
62     }
63 
64     /**
65      * Returns the method's name
66      */
67     @Override
68     public String getName() {
69         return method.getName();
70     }
71 
72     /**
73      * Adds to {@code errors} if this method:
74      * <ul>
75      * <li>is not public, or
76      * <li>takes parameters, or
77      * <li>returns something other than void, or
78      * <li>is static (given {@code isStatic is false}), or
79      * <li>is not static (given {@code isStatic is true}).
80      * </ul>
81      */
82     public void validatePublicVoidNoArg(boolean isStatic, List<Throwable> errors) {
83         validatePublicVoid(isStatic, errors);
84         if (method.getParameterTypes().length != 0) {
85             errors.add(new Exception("Method " + method.getName() + " should have no parameters"));
86         }
87     }
88 
89 
90     /**
91      * Adds to {@code errors} if this method:
92      * <ul>
93      * <li>is not public, or
94      * <li>returns something other than void, or
95      * <li>is static (given {@code isStatic is false}), or
96      * <li>is not static (given {@code isStatic is true}).
97      * </ul>
98      */
99     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 }
217