View Javadoc
1   /*
2    * Copyright (c) 2006 JMockit developers
3    * This file is subject to the terms of the MIT license (see LICENSE.txt).
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 }