View Javadoc
1   /*
2    * MIT License
3    * Copyright (c) 2006-2025 JMockit developers
4    * See LICENSE file for full license text.
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 }