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;
7   
8   import edu.umd.cs.findbugs.annotations.NonNull;
9   import edu.umd.cs.findbugs.annotations.Nullable;
10  
11  import java.util.ArrayList;
12  import java.util.List;
13  import java.util.Map;
14  
15  import mockit.internal.expectations.argumentMatching.ArgumentMatcher;
16  import mockit.internal.expectations.invocation.ExpectedInvocation;
17  import mockit.internal.expectations.invocation.InvocationArguments;
18  
19  import org.checkerframework.checker.index.qual.NonNegative;
20  
21  public abstract class BaseVerificationPhase extends TestOnlyPhase {
22      @NonNull
23      final ReplayPhase replayPhase;
24      @NonNull
25      private final List<VerifiedExpectation> currentVerifiedExpectations;
26      @Nullable
27      Expectation currentVerification;
28      int replayIndex;
29      @Nullable
30      Error pendingError;
31      @Nullable
32      ExpectedInvocation matchingInvocationWithDifferentArgs;
33  
34      BaseVerificationPhase(@NonNull ReplayPhase replayPhase) {
35          super(replayPhase.executionState);
36          this.replayPhase = replayPhase;
37          currentVerifiedExpectations = new ArrayList<>();
38      }
39  
40      @Nullable
41      @Override
42      final Object handleInvocation(@Nullable Object mock, int mockAccess, @NonNull String mockClassDesc,
43              @NonNull String mockNameAndDesc, @Nullable String genericSignature, boolean withRealImpl,
44              @NonNull Object[] args) {
45          if (pendingError != null) {
46              replayPhase.failureState.setErrorThrown(pendingError);
47              pendingError = null;
48              return null;
49          }
50  
51          matchInstance = mock != null && (executionState.equivalentInstances.isReplacementInstance(mock, mockNameAndDesc)
52                  || isEnumElement(mock));
53  
54          ExpectedInvocation currentInvocation = new ExpectedInvocation(mock, mockAccess, mockClassDesc, mockNameAndDesc,
55                  matchInstance, genericSignature, args);
56          currentInvocation.arguments.setMatchers(argMatchers);
57          currentVerification = new Expectation(currentInvocation);
58  
59          currentExpectation = null;
60          currentVerifiedExpectations.clear();
61          List<ExpectedInvocation> matchingInvocationsWithDifferentArgs = findExpectation(mock, mockClassDesc,
62                  mockNameAndDesc, args);
63          argMatchers = null;
64  
65          if (replayPhase.failureState.getErrorThrown() != null) {
66              return null;
67          }
68  
69          if (currentExpectation == null) {
70              pendingError = currentVerification.invocation
71                      .errorForMissingInvocation(matchingInvocationsWithDifferentArgs);
72              currentExpectation = currentVerification;
73          }
74  
75          return currentExpectation.invocation.getDefaultValueForReturnType();
76      }
77  
78      @NonNull
79      abstract List<ExpectedInvocation> findExpectation(@Nullable Object mock, @NonNull String mockClassDesc,
80              @NonNull String mockNameAndDesc, @NonNull Object[] args);
81  
82      final boolean matches(@Nullable Object mock, @NonNull String mockClassDesc, @NonNull String mockNameAndDesc,
83              @NonNull Object[] args, @NonNull Expectation replayExpectation, @Nullable Object replayInstance,
84              @NonNull Object[] replayArgs) {
85          ExpectedInvocation invocation = replayExpectation.invocation;
86          boolean constructor = invocation.isConstructor();
87          Map<Object, Object> replacementMap = getReplacementMap();
88          matchingInvocationWithDifferentArgs = null;
89  
90          if (invocation.isMatch(mock, mockClassDesc, mockNameAndDesc, replacementMap)) {
91              boolean matching;
92  
93              if (mock == null || invocation.instance == null || constructor && !matchInstance) {
94                  matching = true;
95              } else {
96                  matching = executionState.equivalentInstances.areMatchingInstances(matchInstance, invocation.instance,
97                          mock);
98              }
99  
100             if (matching) {
101                 matchingInvocationWithDifferentArgs = invocation;
102 
103                 InvocationArguments invocationArguments = invocation.arguments;
104                 List<ArgumentMatcher<?>> originalMatchers = invocationArguments.getMatchers();
105                 Object[] originalArgs = invocationArguments.prepareForVerification(args, argMatchers);
106                 boolean argumentsMatch = invocationArguments.isMatch(replayArgs, getInstanceMap());
107                 invocationArguments.setValuesAndMatchers(originalArgs, originalMatchers);
108 
109                 if (argumentsMatch) {
110                     addVerifiedExpectation(replayExpectation, replayArgs);
111                     return true;
112                 }
113             }
114         }
115 
116         return false;
117     }
118 
119     abstract void addVerifiedExpectation(@NonNull Expectation expectation, @NonNull Object[] args);
120 
121     final void addVerifiedExpectation(@NonNull VerifiedExpectation verifiedExpectation) {
122         executionState.verifiedExpectations.add(verifiedExpectation);
123         currentVerifiedExpectations.add(verifiedExpectation);
124     }
125 
126     @Override
127     final void setMaxInvocationCount(int maxInvocations) {
128         if (maxInvocations == 0 || pendingError == null) {
129             super.setMaxInvocationCount(maxInvocations);
130         }
131     }
132 
133     @Nullable
134     Error endVerification() {
135         return pendingError;
136     }
137 
138     @Nullable
139     final Object getArgumentValueForCurrentVerification(@NonNegative int parameterIndex) {
140         List<VerifiedExpectation> verifiedExpectations = executionState.verifiedExpectations;
141 
142         if (verifiedExpectations.isEmpty()) {
143             return currentVerification == null ? null
144                     : currentVerification.invocation.getArgumentValues()[parameterIndex];
145         }
146 
147         VerifiedExpectation lastMatched = verifiedExpectations.get(verifiedExpectations.size() - 1);
148         return lastMatched.arguments[parameterIndex];
149     }
150 
151     @NonNull
152     public final <T> List<T> getNewInstancesMatchingVerifiedConstructorInvocation() {
153         List<T> newInstances = new ArrayList<>();
154 
155         for (VerifiedExpectation verifiedExpectation : currentVerifiedExpectations) {
156             // noinspection unchecked
157             T newInstance = (T) verifiedExpectation.captureNewInstance();
158             newInstances.add(newInstance);
159         }
160 
161         return newInstances;
162     }
163 }