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