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

hazendaz / jmockit1 / 533

26 Nov 2025 03:18PM UTC coverage: 72.291% (+0.09%) from 72.2%
533

Pull #422

github

web-flow
Merge 447b13428 into f3aed3cfe
Pull Request #422: Restore @Mock invocation constraints and fix MockedTypeCascade concurrency

5732 of 8416 branches covered (68.11%)

Branch coverage included in aggregate %.

78 of 98 new or added lines in 6 files covered. (79.59%)

6 existing lines in 1 file now uncovered.

11998 of 16110 relevant lines covered (74.48%)

0.74 hits per line

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

93.4
/main/src/main/java/mockit/internal/expectations/state/MockedTypeCascade.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.state;
7

8
import static java.lang.reflect.Modifier.INTERFACE;
9
import static java.lang.reflect.Modifier.PUBLIC;
10
import static java.lang.reflect.Modifier.isPublic;
11

12
import static mockit.asm.types.JavaType.getInternalName;
13
import static mockit.internal.util.Utilities.containsReference;
14
import static mockit.internal.util.Utilities.getClassType;
15

16
import edu.umd.cs.findbugs.annotations.NonNull;
17
import edu.umd.cs.findbugs.annotations.Nullable;
18

19
import java.lang.reflect.Method;
20
import java.lang.reflect.ParameterizedType;
21
import java.lang.reflect.Type;
22
import java.lang.reflect.TypeVariable;
23
import java.util.List;
24
import java.util.Map;
25
import java.util.concurrent.ConcurrentHashMap;
26
import java.util.concurrent.CopyOnWriteArrayList;
27

28
import mockit.internal.expectations.MockingFilters;
29
import mockit.internal.expectations.mocking.CascadingTypeRedefinition;
30
import mockit.internal.expectations.mocking.InstanceFactory;
31
import mockit.internal.reflection.GenericTypeReflection;
32
import mockit.internal.reflection.RealMethodOrConstructor;
33
import mockit.internal.state.TestRun;
34
import mockit.internal.util.DefaultValues;
35

36
public final class MockedTypeCascade {
37
    @NonNull
38
    private static final CascadingTypes CASCADING_TYPES = TestRun.getExecutingTest().getCascadingTypes();
1✔
39
    private static final int PUBLIC_INTERFACE = PUBLIC + INTERFACE;
40

41
    final boolean fromMockField;
42
    @NonNull
43
    private final Type mockedType;
44
    @NonNull
45
    final String mockedTypeDesc;
46
    @Nullable
47
    Class<?> mockedClass;
48
    @Nullable
49
    private GenericTypeReflection genericReflection;
50
    @NonNull
51
    private final Map<String, Type> cascadedTypesAndMocks;
52
    @NonNull
53
    private final List<Object> cascadingInstances;
54

55
    MockedTypeCascade(boolean fromMockField, @NonNull Type mockedType, @NonNull String mockedTypeDesc) {
1✔
56
        this.fromMockField = fromMockField;
1✔
57
        this.mockedType = mockedType;
1✔
58
        this.mockedTypeDesc = mockedTypeDesc;
1✔
59
        cascadedTypesAndMocks = new ConcurrentHashMap<>(4);
1✔
60
        cascadingInstances = new CopyOnWriteArrayList<>();
1✔
61
    }
1✔
62

63
    @Nullable
64
    public static Object getMock(@NonNull String mockedTypeDesc, @NonNull String mockedMethodNameAndDesc,
65
            @Nullable Object mockInstance, @NonNull String returnTypeDesc, @NonNull Class<?> returnType) {
66
        MockedTypeCascade cascade = CASCADING_TYPES.getCascade(mockedTypeDesc, mockInstance);
1✔
67

68
        if (cascade == null) {
1!
UNCOV
69
            return null;
×
70
        }
71

72
        String cascadedReturnTypeDesc = getReturnTypeIfCascadingSupportedForIt(returnTypeDesc);
1✔
73

74
        if (cascadedReturnTypeDesc == null) {
1!
UNCOV
75
            return null;
×
76
        }
77

78
        return cascade.getCascadedInstance(mockedMethodNameAndDesc, cascadedReturnTypeDesc, returnType);
1✔
79
    }
80

81
    @Nullable
82
    public static Object getMock(@NonNull String mockedTypeDesc, @NonNull String mockedMethodNameAndDesc,
83
            @Nullable Object mockInstance, @NonNull String returnTypeDesc, @Nullable String genericSignature) {
84
        char typeCode = returnTypeDesc.charAt(0);
1✔
85

86
        if (typeCode != 'L') {
1!
UNCOV
87
            return null;
×
88
        }
89

90
        MockedTypeCascade cascade = CASCADING_TYPES.getCascade(mockedTypeDesc, mockInstance);
1✔
91

92
        if (cascade == null) {
1✔
93
            return null;
1✔
94
        }
95

96
        String resolvedReturnTypeDesc = null;
1✔
97

98
        if (genericSignature != null) {
1✔
99
            resolvedReturnTypeDesc = cascade.getGenericReturnType(mockedTypeDesc, genericSignature);
1✔
100
        }
101

102
        if (resolvedReturnTypeDesc == null) {
1✔
103
            resolvedReturnTypeDesc = getReturnTypeIfCascadingSupportedForIt(returnTypeDesc);
1✔
104

105
            if (resolvedReturnTypeDesc == null) {
1✔
106
                return null;
1✔
107
            }
108
        } else if (resolvedReturnTypeDesc.charAt(0) == '[') {
1✔
109
            return DefaultValues.computeForArrayType(resolvedReturnTypeDesc);
1✔
110
        }
111

112
        return cascade.getCascadedInstance(mockedMethodNameAndDesc, resolvedReturnTypeDesc, mockInstance);
1✔
113
    }
114

115
    @Nullable
116
    private String getGenericReturnType(@NonNull String ownerTypeDesc, @NonNull String genericSignature) {
117
        String resolvedSignature = getGenericReflection().resolveSignature(ownerTypeDesc, genericSignature);
1✔
118
        String returnTypeDesc = resolvedSignature.substring(resolvedSignature.indexOf(')') + 1);
1✔
119

120
        if (returnTypeDesc.charAt(0) == '[') {
1✔
121
            return returnTypeDesc;
1✔
122
        }
123

124
        String returnTypeName = returnTypeDesc.substring(1, returnTypeDesc.length() - 1);
1✔
125
        return isTypeSupportedForCascading(returnTypeName) ? returnTypeName : null;
1✔
126
    }
127

128
    @NonNull
129
    private synchronized GenericTypeReflection getGenericReflection() {
130
        GenericTypeReflection reflection = genericReflection;
1✔
131

132
        if (reflection == null) {
1✔
133
            Class<?> ownerClass = getClassWithCalledMethod();
1✔
134
            reflection = new GenericTypeReflection(ownerClass, mockedType);
1✔
135
            genericReflection = reflection;
1✔
136
        }
137

138
        return reflection;
1✔
139
    }
140

141
    private static boolean isReturnTypeNotSupportedForCascading(@NonNull Class<?> returnType) {
142
        return MockingFilters.isSubclassOfUnmockable(returnType)
1✔
143
                || !isTypeSupportedForCascading(getInternalName(returnType));
1✔
144
    }
145

146
    @SuppressWarnings("OverlyComplexMethod")
147
    private static boolean isTypeSupportedForCascading(@NonNull String typeName) {
148
        // noinspection SimplifiableIfStatement
149
        if (typeName.contains("/Process") || typeName.endsWith("/Runnable")) {
1✔
150
            return true;
1✔
151
        }
152

153
        return (!typeName.startsWith("java/lang/") || typeName.contains("management"))
1✔
154
                && !typeName.startsWith("java/math/")
1✔
155
                && (!typeName.startsWith("java/util/") || typeName.endsWith("/Date") || typeName.endsWith("/Callable")
1✔
156
                        || typeName.endsWith("Future") || typeName.contains("logging"))
1✔
157
                && !"java/time/Duration".equals(typeName);
1✔
158
    }
159

160
    @Nullable
161
    private static String getReturnTypeIfCascadingSupportedForIt(@NonNull String typeDesc) {
162
        String typeName = typeDesc.substring(1, typeDesc.length() - 1);
1✔
163
        return isTypeSupportedForCascading(typeName) ? typeName : null;
1✔
164
    }
165

166
    @Nullable
167
    private Object getCascadedInstance(@NonNull String methodNameAndDesc, @NonNull String returnTypeInternalName,
168
            @NonNull Class<?> returnClass) {
169
        MockedTypeCascade nextLevel = this;
1✔
170

171
        if (!cascadedTypesAndMocks.containsKey(returnTypeInternalName)) {
1!
172
            cascadedTypesAndMocks.put(returnTypeInternalName, returnClass);
1✔
173
            nextLevel = CASCADING_TYPES.add(returnTypeInternalName, false, returnClass);
1✔
174
        }
175

176
        return nextLevel.createNewCascadedInstanceOrUseNonCascadedOneIfAvailable(methodNameAndDesc, returnClass);
1✔
177
    }
178

179
    @Nullable
180
    private Object getCascadedInstance(@NonNull String methodNameAndDesc, @NonNull String returnTypeInternalName,
181
            @Nullable Object mockInstance) {
182
        MockedTypeCascade nextLevel = this;
1✔
183
        Type returnType = cascadedTypesAndMocks.get(returnTypeInternalName);
1✔
184

185
        if (returnType == null) {
1✔
186
            Class<?> cascadingClass = getClassWithCalledMethod();
1✔
187
            Type genericReturnType = getGenericReturnType(cascadingClass, methodNameAndDesc);
1✔
188

189
            if (genericReturnType == null) {
1✔
190
                return null;
1✔
191
            }
192

193
            Class<?> resolvedReturnType = getClassType(genericReturnType);
1✔
194

195
            if (resolvedReturnType.isAssignableFrom(cascadingClass)) {
1✔
196
                if (mockInstance != null) {
1✔
197
                    return mockInstance;
1✔
198
                }
199

200
                returnType = mockedType;
1✔
201
            } else if (nonPublicTypeReturnedFromPublicInterface(cascadingClass, resolvedReturnType)
1✔
202
                    || isReturnTypeNotSupportedForCascading(resolvedReturnType)) {
1✔
203
                return null;
1✔
204
            } else {
205
                Object defaultReturnValue = DefaultValues.computeForType(resolvedReturnType);
1✔
206

207
                if (defaultReturnValue != null) {
1!
UNCOV
208
                    return defaultReturnValue;
×
209
                }
210

211
                cascadedTypesAndMocks.put(returnTypeInternalName, genericReturnType);
1✔
212
                nextLevel = CASCADING_TYPES.add(returnTypeInternalName, false, genericReturnType);
1✔
213
                returnType = genericReturnType;
1✔
214
            }
215
        } else {
1✔
216
            nextLevel = CASCADING_TYPES.getCascade(returnType);
1✔
217
        }
218

219
        return nextLevel.createNewCascadedInstanceOrUseNonCascadedOneIfAvailable(methodNameAndDesc, returnType);
1✔
220
    }
221

222
    private static boolean nonPublicTypeReturnedFromPublicInterface(@NonNull Class<?> cascadingClass,
223
            @NonNull Class<?> resolvedReturnType) {
224
        return cascadingClass.isInterface() && !isPublic(resolvedReturnType.getModifiers())
1✔
225
                && cascadingClass.getClassLoader() != null && (cascadingClass.getModifiers() & PUBLIC_INTERFACE) != 0
1!
226
                && !resolvedReturnType.isMemberClass();
1✔
227
    }
228

229
    @NonNull
230
    private Class<?> getClassWithCalledMethod() {
231
        if (mockedClass != null) {
1!
UNCOV
232
            return mockedClass;
×
233
        }
234

235
        if (mockedType instanceof Class<?>) {
1✔
236
            return (Class<?>) mockedType;
1✔
237
        }
238

239
        return (Class<?>) ((ParameterizedType) mockedType).getRawType();
1✔
240
    }
241

242
    @Nullable
243
    private Type getGenericReturnType(@NonNull Class<?> cascadingClass, @NonNull String methodNameAndDesc) {
244
        RealMethodOrConstructor realMethod;
245

246
        try {
247
            realMethod = new RealMethodOrConstructor(cascadingClass, methodNameAndDesc);
1✔
248
        } catch (NoSuchMethodException e) {
1✔
249
            return null;
1✔
250
        }
1✔
251

252
        Method cascadingMethod = realMethod.getMember();
1✔
253
        Type genericReturnType = cascadingMethod.getGenericReturnType();
1✔
254

255
        if (genericReturnType instanceof TypeVariable<?>) {
1✔
256
            genericReturnType = getGenericReflection().resolveTypeVariable((TypeVariable<?>) genericReturnType);
1✔
257
        }
258

259
        return genericReturnType == Object.class ? null : genericReturnType;
1✔
260
    }
261

262
    @Nullable
263
    private Object createNewCascadedInstanceOrUseNonCascadedOneIfAvailable(@NonNull String methodNameAndDesc,
264
            @NonNull Type mockedReturnType) {
265
        InstanceFactory instanceFactory = TestRun.mockFixture().findInstanceFactory(mockedReturnType);
1✔
266

267
        if (instanceFactory == null) {
1✔
268
            String methodName = methodNameAndDesc.substring(0, methodNameAndDesc.indexOf('('));
1✔
269
            CascadingTypeRedefinition typeRedefinition = new CascadingTypeRedefinition(methodName, mockedReturnType);
1✔
270
            instanceFactory = typeRedefinition.redefineType();
1✔
271

272
            if (instanceFactory == null) {
1!
UNCOV
273
                return null;
×
274
            }
275
        } else {
1✔
276
            Object lastInstance = instanceFactory.getLastInstance();
1✔
277

278
            if (lastInstance != null) {
1✔
279
                return lastInstance;
1✔
280
            }
281
        }
282

283
        Object cascadedInstance = instanceFactory.create();
1✔
284
        instanceFactory.clearLastInstance();
1✔
285
        addInstance(cascadedInstance);
1✔
286
        TestRun.getExecutingTest().addInjectableMock(cascadedInstance);
1✔
287
        return cascadedInstance;
1✔
288
    }
289

290
    void discardCascadedMocks() {
291
        cascadedTypesAndMocks.clear();
1✔
292
        cascadingInstances.clear();
1✔
293
    }
1✔
294

295
    void addInstance(@NonNull Object cascadingInstance) {
296
        cascadingInstances.add(cascadingInstance);
1✔
297
    }
1✔
298

299
    boolean hasInstance(@NonNull Object cascadingInstance) {
300
        return containsReference(cascadingInstances, cascadingInstance);
1✔
301
    }
302
}
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