Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IllegalStateException using sealed class #564

Closed
PabloMR92 opened this issue Dec 27, 2021 · 11 comments · Fixed by #568
Closed

IllegalStateException using sealed class #564

PabloMR92 opened this issue Dec 27, 2021 · 11 comments · Fixed by #568

Comments

@PabloMR92
Copy link

PabloMR92 commented Dec 27, 2021

Describe the bug
Trying to use a new java 17 feature called sealed classes along with equalsverifier results in an exception due to the inability to extend a class.

Code that triggers the behavior

    public void checkEquals() {
        EqualsVerifier
                .forClass(Child.class)
                .withRedefinedSuperclass()
                .verify();
    }

    sealed abstract class Parent permits Child {

        private final int x;
        private final int y;

        public Parent(int x, int y) {
            this.x = x;
            this.y = y;
        }

        @Override
        public boolean equals(Object obj) {
            return this == obj || obj instanceof Parent p && p.x == x && p.y == y;
        }

        @Override
        public int hashCode() {
            return Objects.hash(x, y);
        }
    }

    final class Child extends Parent {

        private final String z;

        public Child(int x, int y, String z) {
            super(x, y);
            this.z = z;
        }

        @Override
        public boolean equals(Object obj) {
            return this == obj || super.equals(obj) && obj instanceof Child p && p.z == z && p.z == z;
        }

        @Override
        public int hashCode() {
            return Objects.hash(super.hashCode(), z, z);
        }
    }

Error message

-> java.lang.IncompatibleClassChangeError: class com.test.unit.identity.Parent$$DynamicSubclass$1083962448 cannot inherit from sealed class com.test.unit.identity.IdentityTest$Parent

Expected behavior
Not sure if there is a workaround for this, ideally it shouldn't break on sealed classes.

Version
3.8.1

Additional context
Add any other context about the problem here.

@jqno
Copy link
Owner

jqno commented Dec 28, 2021

I haven't had time yet to play around with sealed classes and how they interact with EqualsVerifier. I agree that it shouldn't break on them.
In the mean time, can you please post the full stacktrace? Maybe I can offer a workaround.

@PabloMR92
Copy link
Author

Sure thing thanks for the quick response:

-> java.lang.IllegalStateException: Failed to invoke proxy for public abstract java.lang.Class nl.jqno.equalsverifier.internal.lib.bytebuddy.dynamic.loading.ClassInjector$UsingLookup$MethodHandles$Lookup.defineClass(java.lang.Object,byte[]) throws java.lang.IllegalAccessException

For more information, go to: http://www.jqno.nl/equalsverifier/errormessages

	at nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi.verify(SingleTypeEqualsVerifierApi.java:315)
	at com.test.unit.identity.IdentityTest.checkEquals(IdentityTest.java:25)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
	at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
	at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.lang.IllegalStateException: java.lang.IllegalStateException: Failed to invoke proxy for public abstract java.lang.Class nl.jqno.equalsverifier.internal.lib.bytebuddy.dynamic.loading.ClassInjector$UsingLookup$MethodHandles$Lookup.defineClass(java.lang.Object,byte[]) throws java.lang.IllegalAccessException
	at nl.jqno.equalsverifier.internal.lib.bytebuddy.dynamic.loading.ClassInjector$UsingLookup.injectRaw(ClassInjector.java:1617)
	at nl.jqno.equalsverifier.internal.lib.bytebuddy.dynamic.loading.ClassInjector$AbstractBase.inject(ClassInjector.java:114)
	at nl.jqno.equalsverifier.internal.lib.bytebuddy.dynamic.loading.ClassLoadingStrategy$UsingLookup.load(ClassLoadingStrategy.java:496)
	at nl.jqno.equalsverifier.internal.lib.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:100)
	at nl.jqno.equalsverifier.internal.lib.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6159)
	at nl.jqno.equalsverifier.internal.reflection.Instantiator.giveDynamicSubclass(Instantiator.java:98)
	at nl.jqno.equalsverifier.internal.reflection.Instantiator.of(Instantiator.java:47)
	at nl.jqno.equalsverifier.internal.reflection.InPlaceObjectAccessor.copy(InPlaceObjectAccessor.java:23)
	at nl.jqno.equalsverifier.internal.checkers.HierarchyChecker.getEqualSuper(HierarchyChecker.java:155)
	at nl.jqno.equalsverifier.internal.checkers.HierarchyChecker.checkSuperclass(HierarchyChecker.java:66)
	at nl.jqno.equalsverifier.internal.checkers.HierarchyChecker.check(HierarchyChecker.java:51)
	at nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi.verifyWithExamples(SingleTypeEqualsVerifierApi.java:420)
	at nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi.performVerification(SingleTypeEqualsVerifierApi.java:376)
	at nl.jqno.equalsverifier.api.SingleTypeEqualsVerifierApi.verify(SingleTypeEqualsVerifierApi.java:311)
	... 68 more
Caused by: java.lang.IllegalStateException: Failed to invoke proxy for public abstract java.lang.Class nl.jqno.equalsverifier.internal.lib.bytebuddy.dynamic.loading.ClassInjector$UsingLookup$MethodHandles$Lookup.defineClass(java.lang.Object,byte[]) throws java.lang.IllegalAccessException
	at nl.jqno.equalsverifier.internal.lib.bytebuddy.utility.dispatcher.JavaDispatcher$ProxiedInvocationHandler.invoke(JavaDispatcher.java:1157)
	at jdk.proxy2/jdk.proxy2.$Proxy17.defineClass(Unknown Source)
	at nl.jqno.equalsverifier.internal.lib.bytebuddy.dynamic.loading.ClassInjector$UsingLookup.injectRaw(ClassInjector.java:1615)
	... 81 more
Caused by: java.lang.IncompatibleClassChangeError: class com.test.unit.identity.Parent$$DynamicSubclass$1239759990 cannot inherit from sealed class com.test.unit.identity.IdentityTest$Parent
	at java.base/java.lang.ClassLoader.defineClass0(Native Method)
	at java.base/java.lang.System$2.defineClass(System.java:2307)
	at java.base/java.lang.invoke.MethodHandles$Lookup$ClassDefiner.defineClass(MethodHandles.java:2439)
	at java.base/java.lang.invoke.MethodHandles$Lookup$ClassDefiner.defineClass(MethodHandles.java:2416)
	at java.base/java.lang.invoke.MethodHandles$Lookup.defineClass(MethodHandles.java:1843)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at nl.jqno.equalsverifier.internal.lib.bytebuddy.utility.Invoker$Dispatcher.invoke(Unknown Source)
	at nl.jqno.equalsverifier.internal.lib.bytebuddy.utility.dispatcher.JavaDispatcher$Dispatcher$ForNonStaticMethod.invoke(JavaDispatcher.java:1013)
	at nl.jqno.equalsverifier.internal.lib.bytebuddy.utility.dispatcher.JavaDispatcher$ProxiedInvocationHandler.invoke(JavaDispatcher.java:1142)
	... 83 more

@jqno
Copy link
Owner

jqno commented Dec 28, 2021

Thanks for the quick reply! Unfortunately, none of the workarounds I had in mind seem to work, so I'll have to come up with something else. Given that this is a big thing and I'm currently on holiday, it might take a while.

@PabloMR92
Copy link
Author

No problem at all, thanks for maintaining this library. I'll take a look myself as well to see if I can contribute 👍

@io7m
Copy link

io7m commented Dec 28, 2021

Just a "me too". 😄

I use equalsverifier in something like ~50 different projects, and I've just run into this one. Thanks for maintaining the library! 👍

@jqno
Copy link
Owner

jqno commented Dec 29, 2021

Thanks for the kind words! I'll give it a bump on my todo list :)

@jqno jqno mentioned this issue Jan 4, 2022
@jqno jqno closed this as completed in #568 Jan 4, 2022
@jqno
Copy link
Owner

jqno commented Jan 4, 2022

Just released 3.8.2 which fixes this particular issue. I'll take a closer look at sealed classes later!

@PabloMR92
Copy link
Author

Thanks! Could the same check be performed to prevent the same error, but when trying to test the "parent"?

EqualsVerifier
                .forClass(Parent.class)
                .withRedefinedSubclass(Child.class)
                .verify();

@jqno
Copy link
Owner

jqno commented Jan 19, 2022

Oh, oops, could have thought to try that out myself!

How urgent is this for you? I'm in the midst of a complete re-write of the build files, so I can leverage multi-release jar files to handle sealed class files more elegantly. The reflection code I used before is awful.

@jqno jqno reopened this Jan 19, 2022
@jqno
Copy link
Owner

jqno commented Jan 25, 2022

I just released version 3.8.3, which contains a temporary solution: sealed classes are now just skipped. In a future version, I will see if I can make EqualsVerifier go through the permitted classes so it (indirectly) has some instances of the sealed class to work with.

@jqno
Copy link
Owner

jqno commented Feb 27, 2023

I've just released EqualsVerifier 3.14, which reverts the temporary solution mentioned in the previous comment, and adds proper support for sealed types. Note that EqualsVerifier might now fail on your existing tests with sealed types, because a bunch of checks are now no longer skipped 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants