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.expectations.state;
7   
8   import static mockit.internal.util.Utilities.containsReference;
9   
10  import edu.umd.cs.findbugs.annotations.NonNull;
11  import edu.umd.cs.findbugs.annotations.Nullable;
12  
13  import java.lang.reflect.Type;
14  import java.util.ArrayList;
15  import java.util.IdentityHashMap;
16  import java.util.List;
17  import java.util.Map;
18  
19  import mockit.internal.BaseInvocation;
20  import mockit.internal.expectations.RecordAndReplayExecution;
21  import mockit.internal.expectations.mocking.MockedType;
22  import mockit.internal.expectations.mocking.ParameterTypeRedefinitions;
23  
24  public final class ExecutingTest {
25      @Nullable
26      private RecordAndReplayExecution currentRecordAndReplay;
27      @Nullable
28      private RecordAndReplayExecution recordAndReplayForLastTestMethod;
29  
30      @NonNull
31      private final ThreadLocal<Boolean> shouldIgnoreMockingCallbacks;
32      @NonNull
33      private final ThreadLocal<BaseInvocation> proceedingInvocation;
34      private boolean proceeding;
35  
36      @Nullable
37      private ParameterTypeRedefinitions parameterTypeRedefinitions;
38  
39      @NonNull
40      private final List<Object> regularMocks;
41      @NonNull
42      private final List<Object> injectableMocks;
43      @NonNull
44      private final Map<Object, Object> originalToCapturedInstance;
45      @NonNull
46      private final CascadingTypes cascadingTypes;
47  
48      public ExecutingTest() {
49          shouldIgnoreMockingCallbacks = ThreadLocal.withInitial(() -> false);
50          proceedingInvocation = new ThreadLocal<>();
51          regularMocks = new ArrayList<>();
52          injectableMocks = new ArrayList<>();
53          originalToCapturedInstance = new IdentityHashMap<>(4);
54          cascadingTypes = new CascadingTypes();
55      }
56  
57      @NonNull
58      public RecordAndReplayExecution getOrCreateRecordAndReplay() {
59          if (currentRecordAndReplay == null) {
60              setRecordAndReplay(new RecordAndReplayExecution());
61          }
62  
63          return currentRecordAndReplay;
64      }
65  
66      @Nullable
67      public RecordAndReplayExecution getPreviousRecordAndReplay() {
68          RecordAndReplayExecution previous = currentRecordAndReplay;
69          currentRecordAndReplay = null;
70          return previous;
71      }
72  
73      public void setRecordAndReplay(@Nullable RecordAndReplayExecution newRecordAndReplay) {
74          recordAndReplayForLastTestMethod = null;
75          currentRecordAndReplay = newRecordAndReplay;
76      }
77  
78      @Nullable
79      public RecordAndReplayExecution getCurrentRecordAndReplay() {
80          return currentRecordAndReplay;
81      }
82  
83      public boolean isShouldIgnoreMockingCallbacks() {
84          return shouldIgnoreMockingCallbacks.get();
85      }
86  
87      public boolean setShouldIgnoreMockingCallbacks(boolean flag) {
88          boolean previousFlag = shouldIgnoreMockingCallbacks.get();
89          shouldIgnoreMockingCallbacks.set(flag);
90          return previousFlag;
91      }
92  
93      public boolean isProceedingIntoRealImplementation() {
94          boolean result = proceeding;
95          proceeding = false;
96          return result;
97      }
98  
99      public void markAsProceedingIntoRealImplementation() {
100         proceeding = true;
101     }
102 
103     public void markAsProceedingIntoRealImplementation(@NonNull BaseInvocation invocation) {
104         BaseInvocation previousInvocation = proceedingInvocation.get();
105 
106         if (previousInvocation != null) {
107             invocation.setPrevious(previousInvocation);
108         }
109 
110         proceedingInvocation.set(invocation);
111         proceeding = true;
112     }
113 
114     public boolean shouldProceedIntoRealImplementation(@Nullable Object mock, @NonNull String classDesc) {
115         BaseInvocation pendingInvocation = proceedingInvocation.get();
116 
117         // noinspection SimplifiableIfStatement
118         if (pendingInvocation != null && pendingInvocation.isMethodInSuperclass(mock, classDesc)) {
119             return true;
120         }
121 
122         return isProceedingIntoRealImplementation();
123     }
124 
125     public void clearProceedingState() {
126         BaseInvocation pendingInvocation = proceedingInvocation.get();
127         BaseInvocation previousInvocation = pendingInvocation.getPrevious();
128         proceedingInvocation.set(previousInvocation);
129     }
130 
131     @NonNull
132     public RecordAndReplayExecution getRecordAndReplayForVerifications() {
133         if (currentRecordAndReplay == null) {
134             if (recordAndReplayForLastTestMethod != null) {
135                 currentRecordAndReplay = recordAndReplayForLastTestMethod;
136             } else {
137                 // This should only happen if no expectations at all were created by the whole test, but there is one
138                 // (probably empty)
139                 // verification block.
140                 currentRecordAndReplay = new RecordAndReplayExecution();
141             }
142         }
143 
144         // noinspection LockAcquiredButNotSafelyReleased
145         RecordAndReplayExecution.TEST_ONLY_PHASE_LOCK.lock();
146 
147         return currentRecordAndReplay;
148     }
149 
150     @Nullable
151     public ParameterTypeRedefinitions getParameterRedefinitions() {
152         return parameterTypeRedefinitions;
153     }
154 
155     public void setParameterRedefinitions(@NonNull ParameterTypeRedefinitions redefinitions) {
156         parameterTypeRedefinitions = redefinitions;
157     }
158 
159     public void clearRegularAndInjectableMocks() {
160         regularMocks.clear();
161         injectableMocks.clear();
162     }
163 
164     void addInjectableMock(@NonNull Object mock) {
165         if (!isInjectableMock(mock)) {
166             injectableMocks.add(mock);
167         }
168     }
169 
170     public boolean isInjectableMock(@NonNull Object instance) {
171         return containsReference(injectableMocks, instance);
172     }
173 
174     public boolean isUnmockedInstance(@NonNull Object instance) {
175         return !containsReference(regularMocks, instance) && !isInjectableMock(instance);
176     }
177 
178     public static boolean isInstanceMethodWithStandardBehavior(@Nullable Object mock, @NonNull String nameAndDesc) {
179         return mock != null && nameAndDesc.charAt(0) != '<'
180                 && ("equals(Ljava/lang/Object;)Z hashCode()I toString()Ljava/lang/String;".contains(nameAndDesc)
181                         || mock instanceof Comparable<?> && nameAndDesc.startsWith("compareTo(L")
182                                 && nameAndDesc.endsWith(";)I"));
183     }
184 
185     public void registerMock(@NonNull MockedType mockedType, @NonNull Object mock) {
186         if (mockedType.injectable) {
187             addInjectableMock(mock);
188         } else if (!containsReference(regularMocks, mock)) {
189             regularMocks.add(mock);
190         }
191 
192         Type declaredType = mockedType.getDeclaredType();
193         cascadingTypes.addInstance(declaredType, mock);
194     }
195 
196     @NonNull
197     public CascadingTypes getCascadingTypes() {
198         return cascadingTypes;
199     }
200 
201     public void finishExecution() {
202         recordAndReplayForLastTestMethod = currentRecordAndReplay;
203         currentRecordAndReplay = null;
204 
205         if (parameterTypeRedefinitions != null) {
206             parameterTypeRedefinitions.cleanUp();
207             parameterTypeRedefinitions = null;
208         }
209 
210         cascadingTypes.clearNonSharedCascadingTypes();
211     }
212 
213     public void addCapturedInstanceForInjectableMock(@Nullable Object originalInstance,
214             @NonNull Object capturedInstance) {
215         injectableMocks.add(capturedInstance);
216         addCapturedInstance(originalInstance, capturedInstance);
217     }
218 
219     public void addCapturedInstance(@Nullable Object originalInstance, @NonNull Object capturedInstance) {
220         originalToCapturedInstance.put(capturedInstance, originalInstance);
221     }
222 }