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

Better error message for @ParameterizedTest failures in a @QuarkusTest #38991

Closed
ge0ffrey opened this issue Feb 25, 2024 · 4 comments
Closed
Assignees
Labels
area/testing kind/bug Something isn't working triage/out-of-date This issue/PR is no longer valid or relevant

Comments

@ge0ffrey
Copy link
Contributor

ge0ffrey commented Feb 25, 2024

Describe the bug

I am running a @ParameterizedTest with @QuarkusTest.
I am getting an error. The same code without @QuarkusTest runs successfully, in a normal @ParameterizedTest.

The error message talks about unrelated topics, such as XStream (which we don't use) and cloning.

Expected behavior

A clear, helpful error message with a line number for me to look at.

Actual behavior

A weird XStream cloning exception, with no clue which line is causing it.

We don't use XStream. We don't do cloning here.

com.thoughtworks.xstream.mapper.CannotResolveClassException: org.acme.bedallocation.solver.BedAllocationConstraintProviderTest$$Lambda/0x00007f24e87654f8

	at com.thoughtworks.xstream.mapper.DefaultMapper.realClass(DefaultMapper.java:81)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
	at com.thoughtworks.xstream.mapper.DynamicProxyMapper.realClass(DynamicProxyMapper.java:55)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
	at com.thoughtworks.xstream.mapper.PackageAliasingMapper.realClass(PackageAliasingMapper.java:88)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
	at com.thoughtworks.xstream.mapper.ClassAliasingMapper.realClass(ClassAliasingMapper.java:79)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
	at com.thoughtworks.xstream.mapper.ArrayMapper.realClass(ArrayMapper.java:74)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
	at com.thoughtworks.xstream.mapper.SecurityMapper.realClass(SecurityMapper.java:71)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125)
	at com.thoughtworks.xstream.mapper.CachingMapper.realClass(CachingMapper.java:47)
	at com.thoughtworks.xstream.core.util.HierarchicalStreams.readClassType(HierarchicalStreams.java:29)
	at com.thoughtworks.xstream.core.TreeUnmarshaller.start(TreeUnmarshaller.java:135)
	at com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.unmarshal(AbstractTreeMarshallingStrategy.java:32)
	at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1464)
	at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1441)
	at com.thoughtworks.xstream.XStream.fromXML(XStream.java:1321)
	at com.thoughtworks.xstream.XStream.fromXML(XStream.java:1312)
	at io.quarkus.test.junit.internal.XStreamDeepClone.doClone(XStreamDeepClone.java:54)
	at io.quarkus.test.junit.internal.XStreamDeepClone.clone(XStreamDeepClone.java:39)
	at io.quarkus.test.junit.internal.SerializationWithXStreamFallbackDeepClone.clone(SerializationWithXStreamFallbackDeepClone.java:33)
	at io.quarkus.test.junit.QuarkusTestExtension.runExtensionMethod(QuarkusTestExtension.java:998)
	at io.quarkus.test.junit.QuarkusTestExtension.runExtensionMethod(QuarkusTestExtension.java:907)
	at io.quarkus.test.junit.QuarkusTestExtension.interceptTestTemplateMethod(QuarkusTestExtension.java:867)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
	at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762)
	at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:276)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
	at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:276)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
	at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:276)
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)

How to Reproduce?

Here's the test that I ran on timefold-quickstarts:

@QuarkusTest
class BedAllocationConstraintProviderTest {

    private static final Night ZERO_NIGHT = new Night("0", 0);
    private static final Night FIVE_NIGHT = new Night("5", 5);

    private static final Specialism DEFAULT_SPECIALISM = new Specialism();

    @Inject
    ConstraintVerifier<BedAllocationConstraintProvider, BedAllocationSchedule> constraintVerifier;

    @ParameterizedTest(name = "department = {0}, patientAge = {1}")
    @MethodSource("departmentAgeLimitationProvider")
    void departmentAgeLimitationConstraintTest(Department department, int patientAge,
            BiFunction<BedAllocationConstraintProvider, ConstraintFactory, Constraint> constraintFunction) {

        Room room = new Room();
        room.setDepartment(department);

        Patient patient = new Patient();
        patient.setAge(patientAge);

        Bed bed = new Bed();
        bed.setRoom(room);

        AdmissionPart admission = new AdmissionPart("0", patient, ZERO_NIGHT, FIVE_NIGHT, DEFAULT_SPECIALISM);
        BedDesignation designation = new BedDesignation("0", admission, bed);

        constraintVerifier.verifyThat(constraintFunction)
                .given(designation, department)
                .penalizesBy(6);
    }

    private static Stream<Arguments> departmentAgeLimitationProvider() {
        Department adultDepartment = new Department("1", "Adult department");
        adultDepartment.setMinimumAge(18);

        Department underageDepartment = new Department("2", "Underage department");
        underageDepartment.setMaximumAge(18);

        return Stream.of(
                Arguments.of(adultDepartment, 5,
                        (BiFunction<BedAllocationConstraintProvider, ConstraintFactory, Constraint>) BedAllocationConstraintProvider::departmentMinimumAge),
                Arguments.of(underageDepartment, 42,
                        (BiFunction<BedAllocationConstraintProvider, ConstraintFactory, Constraint>) BedAllocationConstraintProvider::departmentMaximumAge));
    }
}

Output of uname -a or ver

No response

Output of java -version

No response

Quarkus version or git rev

No response

Build tool (ie. output of mvnw --version or gradlew --version)

No response

Additional information

No response

@ge0ffrey ge0ffrey added the kind/bug Something isn't working label Feb 25, 2024
@geoand
Copy link
Contributor

geoand commented Feb 26, 2024

@ParameterizedTest has been a constant thorn in our side unfortunately...

@holly-cummins was looking into a holistic fix, but I am not sure where we are with that, or if it's even possible

@holly-cummins
Copy link
Contributor

I know this error well (unfortunately). It's a side effect of trying to move objects from one classloader to another, with their state intact. User code doesn't do that, but the test code does do that, for reasons. The way the test framework does that cloning is to use XStream serialization. That was always a bit fragile, but recent versions of Java block the private field access that XStream needs.

The reason we need to move objects from one classloader to another is that JUnit loads the classes with one classloader, but then the tests need to run in a different classloader to be in the proper Quarkus classloading environment. I've got a patch which just loads the class in the correct classloader in the first place, which saves a great deal of hassle. Sadly, there was one path where my nice clean logic didn't work, and in order to get that path working, I had to pull a big chunk of the JUnit codebase into our codebase, which seemed too icky. So I got distracted by other things, while I waited for inspiration to strike.

I'll grab this defect and add it to my pile. In the interim, one workaround is to run on a pre-16 JVM, but on Quarkus 3.7+, that's not exactly going to be a viable workaround. (Doh!)

@holly-cummins holly-cummins self-assigned this Feb 26, 2024
@geoand
Copy link
Contributor

geoand commented May 17, 2024

This error definitely won't be the same after #40601 was merged

dmlloyd added a commit to dmlloyd/quarkus that referenced this issue May 21, 2024
Relates to quarkusio#40601, quarkusio#40749, quarkusio#38991. No use waiting for Dependabot. Fixes problem where an NPE can occur in some tests.
@geoand
Copy link
Contributor

geoand commented Nov 6, 2024

Let's close this as the error message is different

@geoand geoand closed this as not planned Won't fix, can't repro, duplicate, stale Nov 6, 2024
@geoand geoand added the triage/out-of-date This issue/PR is no longer valid or relevant label Nov 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/testing kind/bug Something isn't working triage/out-of-date This issue/PR is no longer valid or relevant
Projects
None yet
Development

No branches or pull requests

3 participants