diff --git a/spock-core/src/main/java/org/spockframework/gentyref/GenericTypeReflector.java b/spock-core/src/main/java/org/spockframework/gentyref/GenericTypeReflector.java
index e4a42382af..c2f1dd1832 100644
--- a/spock-core/src/main/java/org/spockframework/gentyref/GenericTypeReflector.java
+++ b/spock-core/src/main/java/org/spockframework/gentyref/GenericTypeReflector.java
@@ -47,18 +47,40 @@ private static Type mapTypeParameters(Type toMapType, Type typeAndParams) {
return erase(toMapType);
} else {
VarMap varMap = new VarMap();
- Type handlingTypeAndParams = typeAndParams;
- while(handlingTypeAndParams instanceof ParameterizedType) {
- ParameterizedType pType = (ParameterizedType)handlingTypeAndParams;
- Class> clazz = (Class>)pType.getRawType(); // getRawType should always be Class
- varMap.addAll(clazz.getTypeParameters(), pType.getActualTypeArguments());
- handlingTypeAndParams = pType.getOwnerType();
- }
- return varMap.map(toMapType);
- }
- }
+ collectVarMapEntriesForTypeVariable(toMapType, varMap);
+ collectVarMapEntriesForType(typeAndParams, varMap);
+ return varMap.map(toMapType);
+ }
+ }
- /**
+ /**
+ * This will collect the {@link TypeVariable} bounds of toMapType
into the varMap
,
+ * if the toMapType
is a {@link TypeVariable} and has exactly one type bound ({@link TypeVariable#getBounds()}.
+ *
+ * @param toMapType Type possibly containing type arguments
+ * @param varMap the {@link VarMap} to save resolve variables into. Caution: This will be modified
+ */
+ private static void collectVarMapEntriesForTypeVariable(Type toMapType, VarMap varMap) {
+ if (toMapType instanceof TypeVariable) {
+ final TypeVariable> t = (TypeVariable>) toMapType;
+ final Type[] bounds = t.getBounds();
+ if (bounds.length == 1) {
+ varMap.add(t, bounds[0]);
+ }
+ }
+ }
+
+ private static void collectVarMapEntriesForType(Type typeAndParams, VarMap varMap) {
+ Type handlingTypeAndParams = typeAndParams;
+ while (handlingTypeAndParams instanceof ParameterizedType) {
+ ParameterizedType pType = (ParameterizedType) handlingTypeAndParams;
+ Class> clazz = (Class>) pType.getRawType(); // getRawType should always be Class
+ varMap.addAll(clazz.getTypeParameters(), pType.getActualTypeArguments());
+ handlingTypeAndParams = pType.getOwnerType();
+ }
+ }
+
+ /**
* Checks if the given type is a class that is supposed to have type parameters, but doesn't.
* In other words, if it's a really raw type.
*/
diff --git a/spock-specs/src/test/groovy/org/spockframework/gentyref/GenericTypeReflectorSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/gentyref/GenericTypeReflectorSpec.groovy
index 0c2ab1b472..20a7ba4814 100644
--- a/spock-specs/src/test/groovy/org/spockframework/gentyref/GenericTypeReflectorSpec.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/gentyref/GenericTypeReflectorSpec.groovy
@@ -16,8 +16,14 @@
package org.spockframework.gentyref
+import org.spockframework.runtime.model.NodeInfo
import spock.lang.Specification
+import java.lang.annotation.Annotation
+import java.lang.reflect.AnnotatedElement
+import java.lang.reflect.Method
+import java.lang.reflect.Type
+
class GenericTypeReflectorSpec extends Specification {
def "can get exact parameter types for java.lang.Object method"() {
when:
@@ -51,4 +57,24 @@ class GenericTypeReflectorSpec extends Specification {
notThrown(NullPointerException)
type == Object
}
+
+ def "getExactReturnType"(Class> clazz, String methodName, Type expectedType) {
+ given:
+ def method = clazz.getMethods().find { it.name == methodName }
+ expect:
+ clazz && methodName && method != null
+ GenericTypeReflector.getExactReturnType(method, clazz) == expectedType
+
+ where:
+ clazz | methodName | expectedType
+ NodeInfo | "getAnnotation" | Annotation
+ NodeInfo | "getName" | String
+ NodeInfo | "getReflection" | AnnotatedElement
+ TestNodeInfo | "getReflection" | Method
+ NodeInfo | "getParent" | NodeInfo
+ TestNodeInfo | "getParent" | TestNodeInfo
+ ArrayList | "get" | Object
+ }
+
+ private abstract class TestNodeInfo extends NodeInfo {}
}
diff --git a/spock-specs/src/test/groovy/org/spockframework/mock/MockSpecInfoAnnotationSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/mock/MockSpecInfoAnnotationSpec.groovy
new file mode 100644
index 0000000000..1bb50a1577
--- /dev/null
+++ b/spock-specs/src/test/groovy/org/spockframework/mock/MockSpecInfoAnnotationSpec.groovy
@@ -0,0 +1,34 @@
+package org.spockframework.mock
+
+import org.spockframework.runtime.model.SpecInfo
+import org.spockframework.util.Nullable
+import spock.lang.Specification
+
+import java.lang.annotation.Annotation
+
+class MockSpecInfoAnnotationSpec extends Specification {
+
+ def "NodeInfo.getAnnotation() shall return valid Stub for Stub Issue #1163"() {
+ given:
+ def mockUtil = new MockUtil()
+ def spec = Stub(SpecInfo)
+
+ when:
+ def t = spec.getAnnotation(Nullable)
+
+ then:
+ t instanceof Annotation
+ mockUtil.isMock(t)
+ }
+
+ def "NodeInfo.getAnnotation() shall return null for Mock Issue #1163"() {
+ given:
+ def spec = Mock(SpecInfo)
+
+ when:
+ def t = spec.getAnnotation(Nullable)
+
+ then:
+ t == null
+ }
+}