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