Fix Mock does not intercept package-private methods in OSGi correctly#2385
Fix Mock does not intercept package-private methods in OSGi correctly#2385AndreasTu wants to merge 1 commit into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
✅ Files skipped from review due to trivial changes (2)
🚧 Files skipped from review as they are similar to previous changes (4)
📝 WalkthroughWalkthroughThe PR updates ByteBuddy mock locality detection, adds an OSGi-style test loader and Java fixtures, and introduces a regression spec that exercises package-private mocking from Groovy and Java. The release notes add the related issue entry. ChangesOSGi package-private mock fix
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@spock-core/src/main/java/org/spockframework/mock/runtime/ByteBuddyMockFactory.java`:
- Around line 103-124: Handle bootstrap-loaded targets in
ByteBuddyMockFactory.isLocalMock before calling
loadClassIfAvailableInClassLoader, since targetClass.getClassLoader() can be
null. Update the null-handling so a bootstrap-loaded target is treated as
non-local without invoking loader.loadClass(), and make
loadClassIfAvailableInClassLoader safely cope with a null ClassLoader. Use the
existing symbols isLocalMock and loadClassIfAvailableInClassLoader to apply the
fix.
In
`@spock-specs/src/test/groovy/org/spockframework/smoke/mock/osgi/OsgiPackagePrivateMemberMockSpec.groovy`:
- Around line 27-30: The embedded spec in OsgiPackagePrivateMemberMockSpec
should explicitly use the ByteBuddy mock maker instead of relying on the
default. Update the Mock call inside the EmbeddedSpecRunner string in
OsgiPackagePrivateMemberMockSpec to pass the mockMaker argument with
spock.mock.MockMakers.byteBuddy so this regression test specifically exercises
the ByteBuddyMockFactory path and does not pass accidentally under a different
default mock maker.
In
`@spock-specs/src/test/groovy/org/spockframework/smoke/mock/osgi/OsgiTestClassLoader.groovy`:
- Around line 45-50: The resource stream opened in
OsgiTestClassLoader.getResourceAsStream is not closed after reading, which can
leave classpath handles open across repeated test runs. Update the class loading
logic around hostCl.getResourceAsStream and defineClass to read the bytes within
a close-safe block, ensuring the stream is always closed even when
ClassNotFoundException is thrown or class data is loaded successfully.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: d0aa8646-b26e-4d7d-b597-1b4677c79e86
📒 Files selected for processing (6)
docs/release_notes.adocspock-core/src/main/java/org/spockframework/mock/runtime/ByteBuddyMockFactory.javaspock-specs/src/test/groovy/org/spockframework/smoke/mock/osgi/OsgiPackagePrivateMemberMockSpec.groovyspock-specs/src/test/groovy/org/spockframework/smoke/mock/osgi/OsgiTestClassLoader.groovyspock-specs/src/test/groovy/org/spockframework/smoke/mock/osgi/testclasses/InvocationFromJava.javaspock-specs/src/test/groovy/org/spockframework/smoke/mock/osgi/testclasses/PkgPrivateMemberClass.java
Greptile SummaryThis PR fixes a bug where Spock mocks fail to intercept package-private methods in OSGi environments by replacing the
Confidence Score: 3/5The core logic change is correct and well-tested for the OSGi scenario, but The fix correctly addresses the OSGi locality check and is backed by a targeted integration test. The missing null guard on the ClassLoader is a real, reproducible NPE (not caught by the existing spock-core/src/main/java/org/spockframework/mock/runtime/ByteBuddyMockFactory.java — specifically Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant BB as ByteBuddyMockFactory
participant DM as determineBestClassLoadingStrategy
participant LM as isLocalMock
participant TL as targetClassLoader
BB->>DM: determineBestClassLoadingStrategy(targetClass, settings)
DM->>LM: isLocalMock(targetClass, additionalInterfaces)
LM->>TL: loadClass(ISpockMockObject.class.name)
TL-->>LM: "loaded Class<?> (or ClassNotFoundException)"
LM->>LM: "identity check: loaded == ISpockMockObject.class?"
loop each additionalInterface
LM->>TL: loadClass(ifClass.name)
TL-->>LM: "loaded Class<?> (or ClassNotFoundException)"
LM->>LM: "identity check: loaded == ifClass?"
end
LM-->>DM: true (local) / false (non-local)
alt "isLocal == true"
DM-->>BB: ClassLoadingStrategy.UsingLookup (package-private access)
else "isLocal == false"
DM-->>BB: ClassLoadingStrategy.INJECTION or WRAPPER
end
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant BB as ByteBuddyMockFactory
participant DM as determineBestClassLoadingStrategy
participant LM as isLocalMock
participant TL as targetClassLoader
BB->>DM: determineBestClassLoadingStrategy(targetClass, settings)
DM->>LM: isLocalMock(targetClass, additionalInterfaces)
LM->>TL: loadClass(ISpockMockObject.class.name)
TL-->>LM: "loaded Class<?> (or ClassNotFoundException)"
LM->>LM: "identity check: loaded == ISpockMockObject.class?"
loop each additionalInterface
LM->>TL: loadClass(ifClass.name)
TL-->>LM: "loaded Class<?> (or ClassNotFoundException)"
LM->>LM: "identity check: loaded == ifClass?"
end
LM-->>DM: true (local) / false (non-local)
alt "isLocal == true"
DM-->>BB: ClassLoadingStrategy.UsingLookup (package-private access)
else "isLocal == false"
DM-->>BB: ClassLoadingStrategy.INJECTION or WRAPPER
end
Reviews (1): Last reviewed commit: "Fix Mock does not intercept package-priv..." | Re-trigger Greptile |
We now do a real search in the mock targets ClassLoader to check if the Spock classes and additional interfaces are visible instead of asking the Parent ClassLoader hierarchy, which does not work in an OSGi environment. Fixes spockframework#2384
We now do a real search in the mock targets ClassLoader to check if the Spock classes and additional interfaces are visible instead of asking the Parent ClassLoader hierarchy, which does not work in an OSGi environment.
Fixes #2384