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