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.expectations.mocking;
6   
7   import static java.lang.reflect.Modifier.isFinal;
8   
9   import edu.umd.cs.findbugs.annotations.NonNull;
10  
11  import java.lang.reflect.Field;
12  import java.util.ArrayList;
13  import java.util.HashMap;
14  import java.util.List;
15  import java.util.Map;
16  import java.util.Map.Entry;
17  
18  import mockit.asm.jvmConstants.Access;
19  import mockit.internal.reflection.FieldReflection;
20  import mockit.internal.state.TestRun;
21  import mockit.internal.util.StackTrace;
22  
23  @SuppressWarnings("UnnecessaryFullyQualifiedName")
24  public final class FieldTypeRedefinitions extends TypeRedefinitions {
25      private static final int FIELD_ACCESS_MASK = Access.SYNTHETIC + Access.STATIC;
26  
27      @NonNull
28      private final Map<MockedType, InstanceFactory> mockInstanceFactories;
29      @NonNull
30      private final List<MockedType> mockFieldsNotSet;
31  
32      public FieldTypeRedefinitions(@NonNull Class<?> testClass) {
33          mockInstanceFactories = new HashMap<>();
34          mockFieldsNotSet = new ArrayList<>();
35  
36          TestRun.enterNoMockingZone();
37  
38          try {
39              redefineFieldTypes(testClass);
40          } finally {
41              TestRun.exitNoMockingZone();
42          }
43      }
44  
45      private void redefineFieldTypes(@NonNull Class<?> classWithMockFields) {
46          Class<?> superClass = classWithMockFields.getSuperclass();
47  
48          if (superClass != null && superClass != Object.class && superClass != mockit.Expectations.class) {
49              redefineFieldTypes(superClass);
50          }
51  
52          Field[] fields = classWithMockFields.getDeclaredFields();
53  
54          for (Field candidateField : fields) {
55              int fieldModifiers = candidateField.getModifiers();
56  
57              if ((fieldModifiers & FIELD_ACCESS_MASK) == 0) {
58                  redefineFieldType(candidateField, fieldModifiers);
59              }
60          }
61      }
62  
63      private void redefineFieldType(@NonNull Field field, int modifiers) {
64          MockedType mockedType = new MockedType(field);
65  
66          if (mockedType.isMockableType()) {
67              boolean partialMocking = field.isAnnotationPresent(mockit.Tested.class);
68              boolean needsValueToSet = !isFinal(modifiers) && !partialMocking;
69  
70              redefineFieldType(mockedType, partialMocking, needsValueToSet);
71  
72              if (!partialMocking) {
73                  registerCaptureOfNewInstances(mockedType);
74              }
75          }
76      }
77  
78      private void redefineFieldType(@NonNull MockedType mockedType, boolean partialMocking, boolean needsValueToSet) {
79          FieldTypeRedefinition typeRedefinition = new FieldTypeRedefinition(mockedType);
80          boolean redefined;
81  
82          if (needsValueToSet) {
83              InstanceFactory factory = typeRedefinition.redefineType();
84              redefined = factory != null;
85  
86              if (redefined) {
87                  mockInstanceFactories.put(mockedType, factory);
88              }
89          } else {
90              if (partialMocking) {
91                  redefined = typeRedefinition.redefineTypeForTestedField();
92              } else {
93                  redefined = typeRedefinition.redefineTypeForFinalField();
94              }
95  
96              if (redefined) {
97                  mockFieldsNotSet.add(mockedType);
98              }
99          }
100 
101         if (redefined) {
102             addTargetClass(mockedType);
103         }
104     }
105 
106     private void registerCaptureOfNewInstances(@NonNull MockedType mockedType) {
107         if (mockedType.getMaxInstancesToCapture() > 0) {
108             if (captureOfNewInstances == null) {
109                 captureOfNewInstances = new CaptureOfNewInstancesForFields();
110             }
111 
112             captureOfNewInstances.registerCaptureOfNewInstances(mockedType, null);
113         }
114     }
115 
116     public void assignNewInstancesToMockFields(@NonNull Object target) {
117         TestRun.getExecutingTest().clearRegularAndInjectableMocks();
118         createAndAssignNewInstances(target);
119         obtainAndRegisterInstancesOfFieldsNotSet(target);
120     }
121 
122     private void createAndAssignNewInstances(@NonNull Object target) {
123         for (Entry<MockedType, InstanceFactory> metadataAndFactory : mockInstanceFactories.entrySet()) {
124             MockedType mockedType = metadataAndFactory.getKey();
125             InstanceFactory instanceFactory = metadataAndFactory.getValue();
126 
127             Object mock = assignNewInstanceToMockField(target, mockedType, instanceFactory);
128             registerMock(mockedType, mock);
129         }
130     }
131 
132     @NonNull
133     private Object assignNewInstanceToMockField(@NonNull Object target, @NonNull MockedType mockedType,
134             @NonNull InstanceFactory instanceFactory) {
135         Field mockField = mockedType.field;
136         assert mockField != null;
137         Object mock = FieldReflection.getFieldValue(mockField, target);
138 
139         if (mock == null) {
140             try {
141                 mock = instanceFactory.create();
142             } catch (NoClassDefFoundError | ExceptionInInitializerError e) {
143                 StackTrace.filterStackTrace(e);
144                 e.printStackTrace();
145                 throw e;
146             }
147 
148             FieldReflection.setFieldValue(mockField, target, mock);
149 
150             if (mockedType.getMaxInstancesToCapture() > 0) {
151                 assert captureOfNewInstances != null;
152                 CaptureOfNewInstancesForFields capture = (CaptureOfNewInstancesForFields) captureOfNewInstances;
153                 capture.resetCaptureCount(mockField);
154             }
155         }
156 
157         return mock;
158     }
159 
160     private void obtainAndRegisterInstancesOfFieldsNotSet(@NonNull Object target) {
161         for (MockedType metadata : mockFieldsNotSet) {
162             assert metadata.field != null;
163             Object mock = FieldReflection.getFieldValue(metadata.field, target);
164 
165             if (mock != null) {
166                 registerMock(metadata, mock);
167             }
168         }
169     }
170 
171     /**
172      * Returns true iff the mock instance concrete class is not mocked in some test, i.e. it's a class which only
173      * appears in the code under test.
174      */
175     public boolean captureNewInstanceForApplicableMockField(@NonNull Object mock) {
176         if (captureOfNewInstances == null) {
177             return false;
178         }
179 
180         Object fieldOwner = TestRun.getCurrentTestInstance();
181         return captureOfNewInstances.captureNewInstance(fieldOwner, mock);
182     }
183 
184     @Override
185     public void cleanUp() {
186         TestRun.getExecutingTest().getCascadingTypes().clear();
187         super.cleanUp();
188     }
189 }