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