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
033 /**
034 * Returns the underlying Java method
035 */
036 public Method getMethod() {
037 return method;
038 }
039
040 /**
041 * Returns the result of invoking this method on {@code target} with
042 * parameters {@code params}. {@link InvocationTargetException}s thrown are
043 * unwrapped, and their causes rethrown.
044 */
045 public Object invokeExplosively(final Object target, final Object... params)
046 throws Throwable {
047 return new ReflectiveCallable() {
048 @Override
049 protected Object runReflectiveCall() throws Throwable {
050 return method.invoke(target, params);
051 }
052 }.run();
053 }
054
055 /**
056 * Returns the method's name
057 */
058 @Override
059 public String getName() {
060 return method.getName();
061 }
062
063 /**
064 * Adds to {@code errors} if this method:
065 * <ul>
066 * <li>is not public, or
067 * <li>takes parameters, or
068 * <li>returns something other than void, or
069 * <li>is static (given {@code isStatic is false}), or
070 * <li>is not static (given {@code isStatic is true}).
071 * </ul>
072 */
073 public void validatePublicVoidNoArg(boolean isStatic, List<Throwable> errors) {
074 validatePublicVoid(isStatic, errors);
075 if (method.getParameterTypes().length != 0) {
076 errors.add(new Exception("Method " + method.getName() + " should have no parameters"));
077 }
078 }
079
080
081 /**
082 * Adds to {@code errors} if this method:
083 * <ul>
084 * <li>is not public, or
085 * <li>returns something other than void, or
086 * <li>is static (given {@code isStatic is false}), or
087 * <li>is not static (given {@code isStatic is true}).
088 * </ul>
089 */
090 public void validatePublicVoid(boolean isStatic, List<Throwable> errors) {
091 if (isStatic() != isStatic) {
092 String state = isStatic ? "should" : "should not";
093 errors.add(new Exception("Method " + method.getName() + "() " + state + " be static"));
094 }
095 if (!isPublic()) {
096 errors.add(new Exception("Method " + method.getName() + "() should be public"));
097 }
098 if (method.getReturnType() != Void.TYPE) {
099 errors.add(new Exception("Method " + method.getName() + "() should be void"));
100 }
101 }
102
103 @Override
104 protected int getModifiers() {
105 return method.getModifiers();
106 }
107
108 /**
109 * Returns the return type of the method
110 */
111 public Class<?> getReturnType() {
112 return method.getReturnType();
113 }
114
115 /**
116 * Returns the return type of the method
117 */
118 @Override
119 public Class<?> getType() {
120 return getReturnType();
121 }
122
123 /**
124 * Returns the class where the method is actually declared
125 */
126 @Override
127 public Class<?> getDeclaringClass() {
128 return method.getDeclaringClass();
129 }
130
131 public void validateNoTypeParametersOnArgs(List<Throwable> errors) {
132 new NoGenericTypeParametersValidator(method).validate(errors);
133 }
134
135 @Override
136 public boolean isShadowedBy(FrameworkMethod other) {
137 if (!other.getName().equals(getName())) {
138 return false;
139 }
140 if (other.getParameterTypes().length != getParameterTypes().length) {
141 return false;
142 }
143 for (int i = 0; i < other.getParameterTypes().length; i++) {
144 if (!other.getParameterTypes()[i].equals(getParameterTypes()[i])) {
145 return false;
146 }
147 }
148 return true;
149 }
150
151 @Override
152 public boolean equals(Object obj) {
153 if (!FrameworkMethod.class.isInstance(obj)) {
154 return false;
155 }
156 return ((FrameworkMethod) obj).method.equals(method);
157 }
158
159 @Override
160 public int hashCode() {
161 return method.hashCode();
162 }
163
164 /**
165 * Returns true if this is a no-arg method that returns a value assignable
166 * to {@code type}
167 *
168 * @deprecated This is used only by the Theories runner, and does not
169 * use all the generic type info that it ought to. It will be replaced
170 * with a forthcoming ParameterSignature#canAcceptResultOf(FrameworkMethod)
171 * once Theories moves to junit-contrib.
172 */
173 @Deprecated
174 public boolean producesType(Type type) {
175 return getParameterTypes().length == 0 && type instanceof Class<?>
176 && ((Class<?>) type).isAssignableFrom(method.getReturnType());
177 }
178
179 private Class<?>[] getParameterTypes() {
180 return method.getParameterTypes();
181 }
182
183 /**
184 * Returns the annotations on this method
185 */
186 public Annotation[] getAnnotations() {
187 return method.getAnnotations();
188 }
189
190 /**
191 * Returns the annotation of type {@code annotationType} on this method, if
192 * one exists.
193 */
194 public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
195 return method.getAnnotation(annotationType);
196 }
197
198 @Override
199 public String toString() {
200 return method.toString();
201 }
202 }