1
2
3
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
173
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 }