• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hazendaz / jmockit1 / 496

15 Nov 2025 05:33PM UTC coverage: 72.192% (-0.008%) from 72.2%
496

push

github

web-flow
Merge pull request #412 from hazendaz/renovate/major-spring-core

Update spring core to v7 (major)

5677 of 8360 branches covered (67.91%)

Branch coverage included in aggregate %.

11922 of 16018 relevant lines covered (74.43%)

0.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.93
/main/src/main/java/mockit/internal/expectations/invocation/ExpectedInvocation.java
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.invocation;
7

8
import static mockit.internal.util.TypeDescriptor.getClassForType;
9

10
import edu.umd.cs.findbugs.annotations.NonNull;
11
import edu.umd.cs.findbugs.annotations.Nullable;
12

13
import java.io.IOException;
14
import java.util.List;
15
import java.util.Map;
16

17
import mockit.asm.types.JavaType;
18
import mockit.internal.expectations.argumentMatching.ArgumentMatcher;
19
import mockit.internal.expectations.state.MockedTypeCascade;
20
import mockit.internal.reflection.GenericTypeReflection;
21
import mockit.internal.reflection.GenericTypeReflection.GenericSignature;
22
import mockit.internal.state.TestRun;
23
import mockit.internal.util.ClassLoad;
24
import mockit.internal.util.DefaultValues;
25
import mockit.internal.util.ObjectMethods;
26
import mockit.internal.util.StackTrace;
27

28
import org.checkerframework.checker.index.qual.NonNegative;
29

30
@SuppressWarnings("OverlyComplexClass")
31
public final class ExpectedInvocation {
32
    @NonNull
33
    private static final Object UNDEFINED_DEFAULT_RETURN = new Object();
1✔
34

35
    @Nullable
36
    public final Object instance;
37
    @Nullable
38
    public Object replacementInstance;
39
    public boolean matchInstance;
40
    @NonNull
41
    public final InvocationArguments arguments;
42
    @Nullable
43
    private final ExpectationError invocationCause;
44
    @Nullable
45
    Object defaultReturnValue;
46

47
    public ExpectedInvocation(@Nullable Object mock, @NonNull String mockedClassDesc, @NonNull String mockNameAndDesc,
48
            @Nullable String genericSignature, @NonNull Object[] args) {
1✔
49
        instance = mock;
1✔
50
        arguments = new InvocationArguments(0, mockedClassDesc, mockNameAndDesc, genericSignature, args);
1✔
51
        invocationCause = null;
1✔
52
        defaultReturnValue = determineDefaultReturnValueFromMethodSignature();
1✔
53
    }
1✔
54

55
    public ExpectedInvocation(@Nullable Object mock, int access, @NonNull String mockedClassDesc,
56
            @NonNull String mockNameAndDesc, boolean matchInstance, @Nullable String genericSignature,
57
            @NonNull Object[] args) {
1✔
58
        instance = mock;
1✔
59
        this.matchInstance = matchInstance;
1✔
60
        arguments = new InvocationArguments(access, mockedClassDesc, mockNameAndDesc, genericSignature, args);
1✔
61
        invocationCause = new ExpectationError();
1✔
62
        defaultReturnValue = determineDefaultReturnValueFromMethodSignature();
1✔
63
    }
1✔
64

65
    @Nullable
66
    public AssertionError getInvocationCause() {
67
        return invocationCause;
×
68
    }
69

70
    @NonNull
71
    private Object determineDefaultReturnValueFromMethodSignature() {
72
        if (instance != null) {
1✔
73
            Object rv = ObjectMethods.evaluateOverride(instance, getMethodNameAndDescription(), getArgumentValues());
1✔
74

75
            if (rv != null) {
1✔
76
                return rv;
1✔
77
            }
78
        }
79

80
        return UNDEFINED_DEFAULT_RETURN;
1✔
81
    }
82

83
    // Simple getters
84
    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
85

86
    @NonNull
87
    public String getClassDesc() {
88
        return arguments.classDesc;
1✔
89
    }
90

91
    @NonNull
92
    public String getClassName() {
93
        return arguments.getClassName();
1✔
94
    }
95

96
    @NonNull
97
    public String getMethodNameAndDescription() {
98
        return arguments.methodNameAndDesc;
1✔
99
    }
100

101
    @NonNull
102
    public Object[] getArgumentValues() {
103
        return arguments.getValues();
1✔
104
    }
105

106
    public boolean isConstructor() {
107
        return arguments.isForConstructor();
1✔
108
    }
109

110
    @NonNull
111
    public String getSignatureWithResolvedReturnType() {
112
        String signature = arguments.genericSignature;
1✔
113

114
        if (signature != null) {
1✔
115
            // TODO: cache it for use in return type conversion, cascading, etc.
116
            String classDesc = getClassDesc();
1✔
117
            Class<?> mockedClass = instance != null ? instance.getClass() : ClassLoad.loadByInternalName(classDesc);
1!
118
            GenericTypeReflection reflection = new GenericTypeReflection(mockedClass, null);
1✔
119
            signature = reflection.resolveSignature(classDesc, signature);
1✔
120

121
            char firstTypeChar = signature.charAt(signature.indexOf(')') + 1);
1✔
122

123
            if (firstTypeChar != 'T' && firstTypeChar != '[') {
1✔
124
                return signature;
1✔
125
            }
126
        }
127

128
        return arguments.methodNameAndDesc;
1✔
129
    }
130

131
    // Matching based on instance or mocked type
132
    // ///////////////////////////////////////////////////////////////////////////////////////////
133

134
    public boolean isMatch(@Nullable Object mock, @NonNull String invokedClassDesc, @NonNull String invokedMethod) {
135
        return (invokedClassDesc.equals(getClassDesc()) || mock != null && TestRun.mockFixture().isCaptured(mock))
1✔
136
                && (isMatchingGenericMethod(mock, invokedMethod) || isMatchingMethod(invokedMethod));
1✔
137
    }
138

139
    private boolean isMatchingGenericMethod(@Nullable Object mock, @NonNull String invokedMethod) {
140
        if (mock != null && instance != null) {
1✔
141
            String genericSignature = arguments.genericSignature;
1✔
142

143
            if (genericSignature != null) {
1✔
144
                Class<?> mockedClass = mock.getClass();
1✔
145

146
                if (mockedClass != instance.getClass()) {
1✔
147
                    GenericTypeReflection typeReflection = new GenericTypeReflection(mockedClass, null);
1✔
148
                    GenericSignature parsedSignature = typeReflection.parseSignature(genericSignature);
1✔
149
                    return parsedSignature.satisfiesSignature(invokedMethod) && isMatchingMethodName(invokedMethod);
1✔
150
                }
151
            }
152
        }
153

154
        return false;
1✔
155
    }
156

157
    private boolean isMatchingMethod(@NonNull String invokedMethod) {
158
        int returnTypeStartPos = getReturnTypePosition(invokedMethod);
1✔
159

160
        if (returnTypeStartPos < 0) {
1✔
161
            return false;
1✔
162
        }
163

164
        if (haveSameReturnTypes(invokedMethod, returnTypeStartPos)) {
1✔
165
            return true;
1✔
166
        }
167

168
        // At this point the methods are known to differ only in return type, so check if the return type of
169
        // the recorded one is assignable to the return type of the one invoked:
170
        return isReturnTypeOfRecordedMethodAssignableToReturnTypeOfInvokedMethod(invokedMethod, returnTypeStartPos);
1✔
171
    }
172

173
    private boolean isMatchingMethodName(@NonNull String invokedMethod) {
174
        int methodNameEndPos = invokedMethod.indexOf('(');
1✔
175
        String methodName = invokedMethod.substring(0, methodNameEndPos + 1);
1✔
176
        return getMethodNameAndDescription().startsWith(methodName);
1✔
177
    }
178

179
    // Returns -1 if the method names or parameters are different.
180
    private int getReturnTypePosition(@NonNull String invokedMethod) {
181
        String recordedMethod = getMethodNameAndDescription();
1✔
182
        int i = 0;
1✔
183

184
        while (true) {
185
            char c = recordedMethod.charAt(i);
1✔
186

187
            if (c != invokedMethod.charAt(i)) {
1✔
188
                return -1;
1✔
189
            }
190

191
            i++;
1✔
192

193
            if (c == ')') {
1✔
194
                return i;
1✔
195
            }
196
        }
1✔
197
    }
198

199
    private boolean haveSameReturnTypes(@NonNull String invokedMethod, @NonNegative int returnTypeStartPos) {
200
        String recordedMethod = getMethodNameAndDescription();
1✔
201
        int n = invokedMethod.length();
1✔
202

203
        if (n != recordedMethod.length()) {
1!
204
            return false;
×
205
        }
206

207
        int j = returnTypeStartPos;
1✔
208

209
        while (true) {
210
            char c = recordedMethod.charAt(j);
1✔
211

212
            if (c != invokedMethod.charAt(j)) {
1✔
213
                return false;
1✔
214
            }
215

216
            j++;
1✔
217

218
            if (j == n) {
1✔
219
                return true;
1✔
220
            }
221
        }
1✔
222
    }
223

224
    private boolean isReturnTypeOfRecordedMethodAssignableToReturnTypeOfInvokedMethod(@NonNull String invokedMethod,
225
            @NonNegative int returnTypeStartPos) {
226
        String recordedMethod = getMethodNameAndDescription();
1✔
227
        JavaType recordedRT = JavaType.getType(recordedMethod.substring(returnTypeStartPos));
1✔
228
        JavaType invokedRT = JavaType.getType(invokedMethod.substring(returnTypeStartPos));
1✔
229

230
        return getClassForType(invokedRT).isAssignableFrom(getClassForType(recordedRT));
1✔
231
    }
232

233
    public boolean isMatch(@NonNull ExpectedInvocation other) {
234
        return isMatch(other.instance, other.getClassDesc(), other.getMethodNameAndDescription(), null);
1✔
235
    }
236

237
    public boolean isMatch(@Nullable Object replayInstance, @NonNull String invokedClassDesc,
238
            @NonNull String invokedMethod, @Nullable Map<Object, Object> replacementMap) {
239
        return isMatch(replayInstance, invokedClassDesc, invokedMethod) && (arguments.isForConstructor()
1✔
240
                || !matchInstance || isEquivalentInstance(replayInstance, replacementMap));
1✔
241
    }
242

243
    private boolean isEquivalentInstance(@Nullable Object mockedInstance,
244
            @Nullable Map<Object, Object> replacementMap) {
245
        return mockedInstance == instance || mockedInstance != null && instance != null && replacementMap != null
1!
246
                && replacementMap.get(mockedInstance) == instance;
1!
247
    }
248

249
    // Creation of Error instances for invocation mismatch reporting
250
    // ///////////////////////////////////////////////////////////////////////
251

252
    @NonNull
253
    public UnexpectedInvocation errorForUnexpectedInvocation() {
254
        String initialMessage = "Unexpected invocation of " + this;
1✔
255
        return newUnexpectedInvocationWithCause("Unexpected invocation", initialMessage);
1✔
256
    }
257

258
    @NonNull
259
    private UnexpectedInvocation newUnexpectedInvocationWithCause(@NonNull String titleForCause,
260
            @NonNull String initialMessage) {
261
        UnexpectedInvocation error = new UnexpectedInvocation(initialMessage);
1✔
262
        setErrorAsInvocationCause(titleForCause, error);
1✔
263
        return error;
1✔
264
    }
265

266
    private void setErrorAsInvocationCause(@NonNull String titleForCause, @NonNull Throwable error) {
267
        if (invocationCause != null) {
1!
268
            invocationCause.defineCause(titleForCause, error);
1✔
269
        }
270
    }
1✔
271

272
    @NonNull
273
    public MissingInvocation errorForMissingInvocation(@NonNull List<ExpectedInvocation> nonMatchingInvocations) {
274
        StringBuilder errorMessage = new StringBuilder(200);
1✔
275
        errorMessage.append("Missing invocation to:\n").append(this);
1✔
276
        appendNonMatchingInvocations(errorMessage, nonMatchingInvocations);
1✔
277

278
        return newMissingInvocationWithCause("Missing invocation", errorMessage.toString());
1✔
279
    }
280

281
    @NonNull
282
    public MissingInvocation errorForMissingInvocations(@NonNegative int missingInvocations,
283
            @NonNull List<ExpectedInvocation> nonMatchingInvocations) {
284
        StringBuilder errorMessage = new StringBuilder(200);
1✔
285
        errorMessage.append("Missing ").append(missingInvocations).append(invocationsTo(missingInvocations))
1✔
286
                .append(this);
1✔
287
        appendNonMatchingInvocations(errorMessage, nonMatchingInvocations);
1✔
288

289
        return newMissingInvocationWithCause("Missing invocations", errorMessage.toString());
1✔
290
    }
291

292
    private void appendNonMatchingInvocations(@NonNull StringBuilder errorMessage,
293
            @NonNull List<ExpectedInvocation> nonMatchingInvocations) {
294
        if (!nonMatchingInvocations.isEmpty()) {
1✔
295
            errorMessage.append("\ninstead got:\n");
1✔
296
            String sep = "";
1✔
297

298
            for (ExpectedInvocation nonMatchingInvocation : nonMatchingInvocations) {
1✔
299
                String invocationDescription = nonMatchingInvocation.toString(instance);
1✔
300
                errorMessage.append(sep).append(invocationDescription);
1✔
301
                sep = "\n";
1✔
302
                nonMatchingInvocation.printCause(errorMessage);
1✔
303
            }
1✔
304
        }
305
    }
1✔
306

307
    @NonNull
308
    private MissingInvocation newMissingInvocationWithCause(@NonNull String titleForCause,
309
            @NonNull String initialMessage) {
310
        MissingInvocation error = new MissingInvocation(initialMessage);
1✔
311
        setErrorAsInvocationCause(titleForCause, error);
1✔
312
        return error;
1✔
313
    }
314

315
    @NonNull
316
    private static String invocationsTo(@NonNegative int invocations) {
317
        return invocations == 1 ? " invocation to:\n" : " invocations to:\n";
1✔
318
    }
319

320
    @NonNull
321
    public UnexpectedInvocation errorForUnexpectedInvocation(@NonNull Object[] replayArgs) {
322
        String message = "Unexpected invocation to:\n" + toString(replayArgs);
1✔
323
        return newUnexpectedInvocationWithCause("Unexpected invocation", message);
1✔
324
    }
325

326
    @NonNull
327
    public UnexpectedInvocation errorForUnexpectedInvocations(@NonNull Object[] replayArgs, int numUnexpected) {
328
        String message = numUnexpected + " unexpected" + invocationsTo(numUnexpected) + toString(replayArgs);
1✔
329
        String titleForCause = numUnexpected == 1 ? "Unexpected invocation" : "Unexpected invocations";
1✔
330
        return newUnexpectedInvocationWithCause(titleForCause, message);
1✔
331
    }
332

333
    @NonNull
334
    @Override
335
    public String toString() {
336
        return toString((Object) null);
1✔
337
    }
338

339
    @NonNull
340
    public String toString(@Nullable Object otherInstance) {
341
        StringBuilder desc = new StringBuilder().append(arguments.toString());
1✔
342

343
        if (instance != otherInstance && instance != null) {
1!
344
            desc.append("\n   on mock instance: ").append(ObjectMethods.objectIdentity(instance));
1✔
345
        }
346

347
        return desc.toString();
1✔
348
    }
349

350
    @NonNull
351
    String toString(@NonNull Object[] actualInvocationArguments) {
352
        Object[] invocationArgs = arguments.getValues();
1✔
353
        List<ArgumentMatcher<?>> matchers = arguments.getMatchers();
1✔
354
        arguments.setValues(actualInvocationArguments);
1✔
355
        arguments.setMatchers(null);
1✔
356
        String description = toString();
1✔
357
        arguments.setMatchers(matchers);
1✔
358
        arguments.setValues(invocationArgs);
1✔
359
        return description;
1✔
360
    }
361

362
    private void printCause(@NonNull Appendable errorMessage) {
363
        if (invocationCause != null) {
1!
364
            try {
365
                errorMessage.append('\n');
1✔
366
            } catch (IOException ignore) {
×
367
            }
1✔
368

369
            StackTrace st = new StackTrace(invocationCause);
1✔
370
            st.filter();
1✔
371
            st.print(errorMessage);
1✔
372
        }
373
    }
1✔
374

375
    // Default result
376
    // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
377

378
    @Nullable
379
    public Object getDefaultValueForReturnType() {
380
        if (defaultReturnValue == UNDEFINED_DEFAULT_RETURN) {
1✔
381
            Class<?> resolvedReturnType = getReturnTypeAsResolvedFromClassArgument();
1✔
382

383
            if (resolvedReturnType != null) {
1✔
384
                defaultReturnValue = DefaultValues.computeForType(resolvedReturnType);
1✔
385

386
                if (defaultReturnValue == null) {
1✔
387
                    String returnTypeDesc = 'L' + resolvedReturnType.getName().replace('.', '/') + ';';
1✔
388
                    String mockedTypeDesc = getClassDesc();
1✔
389
                    defaultReturnValue = MockedTypeCascade.getMock(mockedTypeDesc, arguments.methodNameAndDesc,
1✔
390
                            instance, returnTypeDesc, resolvedReturnType);
391
                }
392

393
                return defaultReturnValue;
1✔
394
            }
395

396
            String returnTypeDesc = DefaultValues.getReturnTypeDesc(arguments.methodNameAndDesc);
1✔
397

398
            if ("V".equals(returnTypeDesc)) {
1✔
399
                return null;
1✔
400
            }
401

402
            defaultReturnValue = DefaultValues.computeForType(returnTypeDesc);
1✔
403

404
            if (defaultReturnValue == null) {
1✔
405
                String mockedTypeDesc = getClassDesc();
1✔
406
                defaultReturnValue = MockedTypeCascade.getMock(mockedTypeDesc, arguments.methodNameAndDesc, instance,
1✔
407
                        returnTypeDesc, arguments.genericSignature);
408
            }
409
        }
410

411
        return defaultReturnValue;
1✔
412
    }
413

414
    @Nullable
415
    private Class<?> getReturnTypeAsResolvedFromClassArgument() {
416
        String genericSignature = arguments.genericSignature;
1✔
417

418
        if (genericSignature != null) {
1✔
419
            int returnTypePos = genericSignature.lastIndexOf(')') + 1;
1✔
420
            char c = genericSignature.charAt(returnTypePos);
1✔
421

422
            if (c == 'T') {
1✔
423
                for (Object arg : arguments.getValues()) {
1✔
424
                    if (arg instanceof Class<?>) {
1✔
425
                        return (Class<?>) arg;
1✔
426
                    }
427
                }
428
            }
429
        }
430

431
        return null;
1✔
432
    }
433

434
    public void copyDefaultReturnValue(@NonNull ExpectedInvocation other) {
435
        defaultReturnValue = other.defaultReturnValue;
1✔
436
    }
1✔
437
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc