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 static java.util.Collections.emptyList;
9   
10  import edu.umd.cs.findbugs.annotations.NonNull;
11  import edu.umd.cs.findbugs.annotations.Nullable;
12  
13  import java.util.Collections;
14  import java.util.List;
15  
16  import mockit.internal.expectations.invocation.ExpectedInvocation;
17  
18  import org.checkerframework.checker.index.qual.NonNegative;
19  
20  final class OrderedVerificationPhase extends BaseVerificationPhase {
21      @NonNegative
22      private final int expectationCount;
23      @NonNegative
24      private int indexIncrement;
25  
26      OrderedVerificationPhase(@NonNull ReplayPhase replayPhase) {
27          super(replayPhase);
28          discardExpectationsAndArgumentsAlreadyVerified(replayPhase.invocations);
29          expectationCount = replayPhase.invocations.size();
30          indexIncrement = 1;
31      }
32  
33      private void discardExpectationsAndArgumentsAlreadyVerified(List<Expectation> expectationsInReplayOrder) {
34          for (VerifiedExpectation verified : executionState.verifiedExpectations) {
35              int i = expectationsInReplayOrder.indexOf(verified.expectation);
36  
37              if (i >= 0) {
38                  expectationsInReplayOrder.set(i, null);
39              }
40          }
41      }
42  
43      @NonNull
44      @Override
45      List<ExpectedInvocation> findExpectation(@Nullable Object mock, @NonNull String mockClassDesc,
46              @NonNull String mockNameAndDesc, @NonNull Object[] args) {
47          Expectation expectation = currentVerification;
48          int i = replayIndex;
49  
50          while (i >= 0 && i < expectationCount) {
51              Expectation replayExpectation = replayPhase.invocations.get(i);
52              Object replayInstance = replayPhase.invocationInstances.get(i);
53              Object[] replayArgs = replayPhase.invocationArguments.get(i);
54  
55              i += indexIncrement;
56  
57              if (replayExpectation == null) {
58                  continue;
59              }
60  
61              if (!matchInstance && executionState.isToBeMatchedOnInstance(mock, mockNameAndDesc)) {
62                  matchInstance = true;
63              }
64  
65              if (matches(mock, mockClassDesc, mockNameAndDesc, args, replayExpectation, replayInstance, replayArgs)) {
66                  currentExpectation = replayExpectation;
67                  i += 1 - indexIncrement;
68                  indexIncrement = 1;
69                  replayIndex = i;
70  
71                  if (expectation != null) {
72                      expectation.constraints.invocationCount++;
73                  }
74  
75                  break;
76              }
77          }
78  
79          return emptyList();
80      }
81  
82      @Override
83      void addVerifiedExpectation(@NonNull Expectation expectation, @NonNull Object[] args) {
84          VerifiedExpectation verifiedExpectation = new VerifiedExpectation(expectation, args, argMatchers, replayIndex);
85          addVerifiedExpectation(verifiedExpectation);
86      }
87  
88      @Override
89      @SuppressWarnings("OverlyComplexMethod")
90      void handleInvocationCountConstraint(int minInvocations, int maxInvocations) {
91          Error errorThrown = pendingError;
92          pendingError = null;
93  
94          if (errorThrown != null && minInvocations > 0) {
95              throw errorThrown;
96          }
97  
98          Expectation verifying = currentVerification;
99  
100         if (verifying == null) {
101             return;
102         }
103 
104         ExpectedInvocation invocation = verifying.invocation;
105         argMatchers = invocation.arguments.getMatchers();
106         int invocationCount = 1;
107 
108         while (replayIndex < expectationCount) {
109             Expectation replayExpectation = replayPhase.invocations.get(replayIndex);
110 
111             if (replayExpectation != null && matchesCurrentVerification(invocation, replayExpectation)) {
112                 invocationCount++;
113                 verifying.constraints.invocationCount++;
114 
115                 if (invocationCount > maxInvocations) {
116                     if (maxInvocations >= 0) {
117                         throw replayExpectation.invocation.errorForUnexpectedInvocation();
118                     }
119 
120                     break;
121                 }
122             } else if (invocationCount >= minInvocations) {
123                 break;
124             }
125 
126             replayIndex++;
127         }
128 
129         argMatchers = null;
130 
131         int n = minInvocations - invocationCount;
132 
133         if (n > 0) {
134             throw invocation.errorForMissingInvocations(n, Collections.<ExpectedInvocation> emptyList());
135         }
136 
137         verifyMaxInvocations(verifying, maxInvocations);
138     }
139 
140     private boolean matchesCurrentVerification(@NonNull ExpectedInvocation invocation,
141             @NonNull Expectation replayExpectation) {
142         Object mock = invocation.instance;
143         String mockClassDesc = invocation.getClassDesc();
144         String mockNameAndDesc = invocation.getMethodNameAndDescription();
145         Object[] args = invocation.arguments.getValues();
146         matchInstance = invocation.matchInstance;
147 
148         if (executionState.isToBeMatchedOnInstance(mock, mockNameAndDesc)) {
149             matchInstance = true;
150         }
151 
152         Object replayInstance = replayPhase.invocationInstances.get(replayIndex);
153         Object[] replayArgs = replayPhase.invocationArguments.get(replayIndex);
154 
155         return matches(mock, mockClassDesc, mockNameAndDesc, args, replayExpectation, replayInstance, replayArgs);
156     }
157 
158     private void verifyMaxInvocations(@NonNull Expectation verifying, int maxInvocations) {
159         if (maxInvocations >= 0) {
160             int n = verifying.constraints.invocationCount - maxInvocations;
161 
162             if (n > 0) {
163                 Object[] replayArgs = replayPhase.invocationArguments.get(replayIndex - 1);
164                 throw verifying.invocation.errorForUnexpectedInvocations(replayArgs, n);
165             }
166         }
167     }
168 }