Skip to content

Commit

Permalink
Merge pull request #16302 from famod/test-paramresolver-record
Browse files Browse the repository at this point in the history
Junit5 parameter: Introduce serialization alternative to XStream
  • Loading branch information
geoand authored Apr 13, 2021
2 parents eb169e8 + 945022c commit 40a58de
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;

import javax.inject.Inject;
Expand All @@ -19,14 +21,14 @@
import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
@ExtendWith(ParameterResolverTest.UnusedBeanDummyInputResolver.class)
@ExtendWith(ParameterResolverTest.SupplierParameterResolver.class)
public class ParameterResolverTest {

@Inject
UnusedBean unusedBean;

@Test
@ExtendWith(ParameterResolverTest.UnusedBeanDummyInputResolver.class)
@ExtendWith(ParameterResolverTest.SupplierParameterResolver.class)
public void testParameterResolver(UnusedBean.DummyInput dummyInput, Supplier<UnusedBean.DummyInput> supplier) {
UnusedBean.DummyResult dummyResult = unusedBean.dummy(dummyInput);
assertEquals("whatever/6", dummyResult.getResult());
Expand All @@ -35,6 +37,20 @@ public void testParameterResolver(UnusedBean.DummyInput dummyInput, Supplier<Unu
assertEquals("fromSupplier/0", dummyResult.getResult());
}

@Test
@ExtendWith(ParameterResolverTest.SomeSerializableParameterResolver.class)
public void testSerializableParameterResolver(SomeSerializable someSerializable) {
assertEquals("foo", someSerializable.value);
assertEquals("nested-foo", someSerializable.nested.value);
}

@Test
@ExtendWith(ParameterResolverTest.ListWithNonSerializableParameterResolver.class)
public void testSerializableParameterResolverFallbackToXStream(List<NonSerializable> list) {
assertEquals("foo", list.get(0).value);
assertEquals("bar", list.get(1).value);
}

public static class UnusedBeanDummyInputResolver implements ParameterResolver {

@Override
Expand Down Expand Up @@ -66,4 +82,62 @@ public Object resolveParameter(ParameterContext parameterContext, ExtensionConte
}
}

public static class SomeSerializableParameterResolver implements ParameterResolver {

@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
return SomeSerializable.class.getName().equals(parameterContext.getParameter().getType().getName());
}

@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
return new SomeSerializable("foo", new SomeSerializable.SomeNestedSerializable("nested-foo"));
}
}

public static class ListWithNonSerializableParameterResolver implements ParameterResolver {

@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
return List.class.isAssignableFrom(parameterContext.getParameter().getType());
}

@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
return Arrays.asList(new NonSerializable("foo"), new NonSerializable("bar"));
}
}

public static class SomeSerializable implements Serializable {

private final String value;
private final SomeNestedSerializable nested;

public SomeSerializable(String value, SomeNestedSerializable nested) {
this.value = value;
this.nested = nested;
}

public static class SomeNestedSerializable implements Serializable {

private final String value;

public SomeNestedSerializable(String value) {
this.value = value;
}
}
}

public static class NonSerializable {

private final String value;

public NonSerializable(String value) {
this.value = value;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
import io.quarkus.test.junit.callback.QuarkusTestBeforeEachCallback;
import io.quarkus.test.junit.callback.QuarkusTestMethodContext;
import io.quarkus.test.junit.internal.DeepClone;
import io.quarkus.test.junit.internal.XStreamDeepClone;
import io.quarkus.test.junit.internal.SerializationWithXStreamFallbackDeepClone;

public class QuarkusTestExtension
implements BeforeEachCallback, AfterEachCallback, BeforeAllCallback, InvocationInterceptor, AfterAllCallback,
Expand Down Expand Up @@ -433,9 +433,8 @@ private List<Object> getAdditionalTestResources(
}
}

// keep it super simple for now, but we might need multiple strategies in the future
private void populateDeepCloneField(StartupAction startupAction) {
deepClone = new XStreamDeepClone(startupAction.getClassLoader());
deepClone = new SerializationWithXStreamFallbackDeepClone(startupAction.getClassLoader());
}

private void populateCallbacks(ClassLoader classLoader) throws ClassNotFoundException {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package io.quarkus.test.junit.internal;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;

/**
* Cloning strategy that just serializes and deserializes using plain old java serialization.
*/
class SerializationDeepClone implements DeepClone {

private final ClassLoader classLoader;

SerializationDeepClone(ClassLoader classLoader) {
this.classLoader = classLoader;
}

@Override
public Object clone(Object objectToClone) {
ByteArrayOutputStream byteOut = new ByteArrayOutputStream(512);
try (ObjectOutputStream objOut = new ObjectOutputStream(byteOut)) {
objOut.writeObject(objectToClone);
try (ObjectInputStream objIn = new ClassLoaderAwareObjectInputStream(byteOut)) {
return objIn.readObject();
}
} catch (IOException | ClassNotFoundException e) {
throw new IllegalStateException("Unable to deep clone object of type '" + objectToClone.getClass().getName()
+ "'. Please report the issue on the Quarkus issue tracker.", e);
}
}

private class ClassLoaderAwareObjectInputStream extends ObjectInputStream {

public ClassLoaderAwareObjectInputStream(ByteArrayOutputStream byteOut) throws IOException {
super(new ByteArrayInputStream(byteOut.toByteArray()));
}

@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws ClassNotFoundException {
return Class.forName(desc.getName(), true, classLoader);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.quarkus.test.junit.internal;

import java.io.Serializable;
import java.util.Optional;

import org.jboss.logging.Logger;

/**
* Cloning strategy delegating to {@link SerializationDeepClone}, falling back to {@link XStreamDeepClone} in case of error.
*/
public class SerializationWithXStreamFallbackDeepClone implements DeepClone {

private static final Logger LOG = Logger.getLogger(SerializationWithXStreamFallbackDeepClone.class);

private final SerializationDeepClone serializationDeepClone;
private final XStreamDeepClone xStreamDeepClone;

public SerializationWithXStreamFallbackDeepClone(ClassLoader classLoader) {
this.serializationDeepClone = new SerializationDeepClone(classLoader);
this.xStreamDeepClone = new XStreamDeepClone(classLoader);
}

@Override
public Object clone(Object objectToClone) {
if (objectToClone instanceof Serializable) {
try {
return serializationDeepClone.clone(objectToClone);
} catch (RuntimeException re) {
LOG.debugf("SerializationDeepClone failed (will fall back to XStream): %s",
Optional.ofNullable(re.getCause()).orElse(re));
}
}
return xStreamDeepClone.clone(objectToClone);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
/**
* Super simple cloning strategy that just serializes to XML and deserializes it using xstream
*/
public class XStreamDeepClone implements DeepClone {
class XStreamDeepClone implements DeepClone {

private final Supplier<XStream> xStreamSupplier;

public XStreamDeepClone(ClassLoader classLoader) {
XStreamDeepClone(ClassLoader classLoader) {
// avoid doing any work eagerly since the cloner is rarely used
xStreamSupplier = () -> {
XStream result = new XStream();
Expand All @@ -22,6 +22,7 @@ public XStreamDeepClone(ClassLoader classLoader) {
};
}

@Override
public Object clone(Object objectToClone) {
if (objectToClone == null) {
return null;
Expand Down

0 comments on commit 40a58de

Please sign in to comment.