1
2
3
4
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.invocation.ExpectedInvocation;
16 import mockit.internal.expectations.invocation.InvocationConstraints;
17 import mockit.internal.expectations.invocation.UnexpectedInvocation;
18
19 final class ReplayPhase extends Phase {
20 @NonNull
21 final FailureState failureState;
22 @NonNull
23 final List<Expectation> invocations;
24 @NonNull
25 final List<Object> invocationInstances;
26 @NonNull
27 final List<Object[]> invocationArguments;
28
29 ReplayPhase(@NonNull PhasedExecutionState executionState, @NonNull FailureState failureState) {
30 super(executionState);
31 this.failureState = failureState;
32 invocations = new ArrayList<>();
33 invocationInstances = new ArrayList<>();
34 invocationArguments = new ArrayList<>();
35 }
36
37 @Override
38 @Nullable
39 Object handleInvocation(@Nullable Object mock, int mockAccess, @NonNull String mockClassDesc,
40 @NonNull String mockNameAndDesc, @Nullable String genericSignature, boolean withRealImpl,
41 @NonNull Object[] args) throws Throwable {
42 Expectation expectation = executionState.findExpectation(mock, mockClassDesc, mockNameAndDesc, args);
43 Object replacementInstance = mock == null ? null
44 : executionState.equivalentInstances.getReplacementInstanceForMethodInvocation(mock, mockNameAndDesc);
45
46 if (expectation == null) {
47 expectation = createExpectation(replacementInstance == null ? mock : replacementInstance, mockAccess,
48 mockClassDesc, mockNameAndDesc, genericSignature, args);
49 } else if (expectation.recordPhase != null) {
50 registerNewInstanceAsEquivalentToOneFromRecordedConstructorInvocation(mock, expectation.invocation);
51 }
52
53 invocations.add(expectation);
54 invocationInstances.add(mock);
55 invocationArguments.add(args);
56 expectation.constraints.incrementInvocationCount();
57
58 return produceResult(expectation, mock, withRealImpl, args);
59 }
60
61 @NonNull
62 private Expectation createExpectation(@Nullable Object mock, int mockAccess, @NonNull String mockClassDesc,
63 @NonNull String mockNameAndDesc, @Nullable String genericSignature, @NonNull Object[] args) {
64 ExpectedInvocation invocation = new ExpectedInvocation(mock, mockAccess, mockClassDesc, mockNameAndDesc, false,
65 genericSignature, args);
66 Expectation expectation = new Expectation(invocation);
67 executionState.addExpectation(expectation);
68 return expectation;
69 }
70
71 private void registerNewInstanceAsEquivalentToOneFromRecordedConstructorInvocation(@Nullable Object mock,
72 @NonNull ExpectedInvocation invocation) {
73 if (mock != null && invocation.isConstructor()) {
74 Map<Object, Object> instanceMap = getInstanceMap();
75 instanceMap.put(mock, invocation.instance);
76 }
77 }
78
79 @Nullable
80 private Object produceResult(@NonNull Expectation expectation, @Nullable Object mock, boolean withRealImpl,
81 @NonNull Object[] args) throws Throwable {
82 boolean executeRealImpl = withRealImpl && expectation.recordPhase == null;
83
84 if (executeRealImpl) {
85 expectation.executedRealImplementation = true;
86 return Void.class;
87 }
88
89 if (expectation.constraints.isInvocationCountMoreThanMaximumExpected()) {
90 UnexpectedInvocation unexpectedInvocation = expectation.invocation.errorForUnexpectedInvocation(args);
91 failureState.setErrorThrown(unexpectedInvocation);
92 return null;
93 }
94
95 return expectation.produceResult(mock, args);
96 }
97
98 @Nullable
99 Error endExecution() {
100 return getErrorForFirstExpectationThatIsMissing();
101 }
102
103 @Nullable
104 private Error getErrorForFirstExpectationThatIsMissing() {
105 List<Expectation> notStrictExpectations = executionState.expectations;
106
107
108 for (Expectation notStrict : notStrictExpectations) {
109 InvocationConstraints constraints = notStrict.constraints;
110
111 if (constraints.isInvocationCountLessThanMinimumExpected()) {
112 List<ExpectedInvocation> nonMatchingInvocations = getNonMatchingInvocations(notStrict);
113 return constraints.errorForMissingExpectations(notStrict.invocation, nonMatchingInvocations);
114 }
115 }
116
117 return null;
118 }
119
120 @NonNull
121 private List<ExpectedInvocation> getNonMatchingInvocations(@NonNull Expectation unsatisfiedExpectation) {
122 ExpectedInvocation unsatisfiedInvocation = unsatisfiedExpectation.invocation;
123 List<ExpectedInvocation> nonMatchingInvocations = new ArrayList<>();
124
125 for (Expectation replayedExpectation : invocations) {
126 ExpectedInvocation replayedInvocation = replayedExpectation.invocation;
127
128 if (replayedExpectation != unsatisfiedExpectation && replayedInvocation.isMatch(unsatisfiedInvocation)) {
129 nonMatchingInvocations.add(replayedInvocation);
130 }
131 }
132
133 return nonMatchingInvocations;
134 }
135 }