1
2
3
4
5
6 package mockit.internal.injection;
7
8 import static mockit.internal.injection.TestedObject.getTestedAnnotationIfPresent;
9
10 import edu.umd.cs.findbugs.annotations.NonNull;
11
12 import java.lang.annotation.Annotation;
13 import java.lang.reflect.Field;
14 import java.lang.reflect.Method;
15 import java.lang.reflect.ParameterizedType;
16 import java.lang.reflect.Type;
17 import java.util.ArrayList;
18 import java.util.LinkedList;
19 import java.util.List;
20
21 import mockit.Injectable;
22 import mockit.Tested;
23 import mockit.asm.jvmConstants.Access;
24 import mockit.internal.expectations.mocking.MockedType;
25
26 public final class TestedClassInstantiations {
27 private static final int FIELD_ACCESS_MASK = Access.SYNTHETIC + Access.STATIC;
28 private static final int METHOD_ACCESS_MASK = Access.BRIDGE + Access.VARARGS + Access.NATIVE + Access.ABSTRACT
29 + Access.SYNTHETIC;
30
31 @NonNull
32 private final List<TestedField> testedFields;
33 @NonNull
34 private final List<InjectionProvider> injectableFields;
35 @NonNull
36 final InjectionState injectionState;
37
38 public TestedClassInstantiations() {
39 testedFields = new LinkedList<>();
40 injectableFields = new ArrayList<>();
41 injectionState = new InjectionState();
42 }
43
44 public boolean findTestedAndInjectableMembers(@NonNull Class<?> testClass) {
45 findAllTestedAndInjectableMembersInTestClassHierarchy(testClass);
46
47 return injectionState.injectionProviders.setInjectables(injectableFields) || !testedFields.isEmpty()
48 || injectionState.interfaceResolution.canResolveInterfaces();
49 }
50
51 private void findAllTestedAndInjectableMembersInTestClassHierarchy(@NonNull Class<?> testClass) {
52 Class<?> superclass = testClass.getSuperclass();
53
54 if (superclass.getClassLoader() != null) {
55 findAllTestedAndInjectableMembersInTestClassHierarchy(superclass);
56 }
57
58 examineInstanceFields(testClass);
59 examineMethods(testClass);
60 }
61
62 private void examineInstanceFields(@NonNull Class<?> testClass) {
63 for (Field candidateField : testClass.getDeclaredFields()) {
64 if ((candidateField.getModifiers() & FIELD_ACCESS_MASK) == 0) {
65 addAsTestedOrInjectableFieldIfApplicable(candidateField);
66 }
67 }
68 }
69
70 private void examineMethods(@NonNull Class<?> testClass) {
71 for (Method candidateMethod : testClass.getDeclaredMethods()) {
72 if ((candidateMethod.getModifiers() & METHOD_ACCESS_MASK) == 0) {
73 addAsTestedMethodIfApplicable(candidateMethod);
74 }
75 }
76 }
77
78 private void addAsTestedOrInjectableFieldIfApplicable(@NonNull Field fieldFromTestClass) {
79 for (Annotation fieldAnnotation : fieldFromTestClass.getDeclaredAnnotations()) {
80 if (fieldAnnotation instanceof Injectable) {
81 InjectionProvider mockedType = new MockedType(fieldFromTestClass);
82 injectableFields.add(mockedType);
83 break;
84 }
85
86 Tested testedMetadata = getTestedAnnotationIfPresent(fieldAnnotation);
87
88 if (testedMetadata != null) {
89 TestedField testedField = new TestedField(injectionState, fieldFromTestClass, testedMetadata);
90 testedFields.add(testedField);
91 break;
92 }
93 }
94 }
95
96 private void addAsTestedMethodIfApplicable(@NonNull Method methodFromTestClass) {
97 for (Annotation methodAnnotation : methodFromTestClass.getDeclaredAnnotations()) {
98 Tested testedMetadata = getTestedAnnotationIfPresent(methodAnnotation);
99
100 if (testedMetadata != null) {
101 addTestedMethodIfApplicable(methodFromTestClass);
102 break;
103 }
104 }
105 }
106
107 private void addTestedMethodIfApplicable(@NonNull Method methodFromTestClass) {
108 Class<?> returnType = methodFromTestClass.getReturnType();
109
110 if (returnType == Class.class) {
111 Type[] parameterTypes = methodFromTestClass.getGenericParameterTypes();
112
113 if (parameterTypes.length == 1) {
114 Type parameterType = parameterTypes[0];
115
116 if (parameterType instanceof ParameterizedType) {
117 ParameterizedType interfaceType = (ParameterizedType) parameterType;
118
119 if (interfaceType.getRawType() == Class.class) {
120 injectionState.interfaceResolution.addInterfaceResolutionMethod(interfaceType,
121 methodFromTestClass);
122 }
123 }
124 }
125 }
126 }
127
128 public void assignNewInstancesToTestedFieldsFromBaseClasses(@NonNull Object testClassInstance) {
129 injectionState.setInjectables(testClassInstance, injectableFields);
130
131 Class<?> testClass = testClassInstance.getClass();
132
133 for (TestedField testedField : testedFields) {
134 if (testedField.isFromBaseClass(testClass)) {
135 instantiateTestedObject(testClassInstance, testedField);
136 }
137 }
138 }
139
140 public void assignNewInstancesToTestedFields(@NonNull Object testClassInstance, boolean beforeSetup,
141 @NonNull List<? extends InjectionProvider> injectableParameters) {
142 List<InjectionProvider> injectables = injectableFields;
143
144 if (!injectableParameters.isEmpty()) {
145 injectables = new ArrayList<>(injectables);
146 injectables.addAll(injectableParameters);
147 }
148
149 injectionState.setInjectables(testClassInstance, injectables);
150
151 for (TestedField testedField : testedFields) {
152 if (!beforeSetup || testedField.isAvailableDuringSetup()) {
153 instantiateTestedObject(testClassInstance, testedField);
154 }
155 }
156 }
157
158 private void instantiateTestedObject(@NonNull Object testClassInstance, @NonNull TestedObject testedObject) {
159 try {
160 testedObject.instantiateWithInjectableValues(testClassInstance);
161 } finally {
162 injectionState.injectionProviders.resetConsumedInjectionProviders();
163 }
164 }
165
166 public void clearTestedObjects() {
167 injectionState.lifecycleMethods.executeTerminationMethodsIfAny();
168 injectionState.clearTestedObjectsAndInstantiatedDependencies();
169 resetTestedFields(false);
170 }
171
172 private void resetTestedFields(boolean duringTearDown) {
173 Object testClassInstance = injectionState.getCurrentTestClassInstance();
174
175 if (testClassInstance != null) {
176 for (TestedObject testedField : testedFields) {
177 testedField.clearIfAutomaticCreation(testClassInstance, duringTearDown);
178 }
179 }
180 }
181
182 public void clearTestedObjectsCreatedDuringSetup() {
183 resetTestedFields(true);
184 }
185
186 @NonNull
187 public BeanExporter getBeanExporter() {
188 return injectionState.getBeanExporter();
189 }
190 }