Skip to content

Commit

Permalink
Add tests + doc notes around "new-ish" collection types used with `@P…
Browse files Browse the repository at this point in the history
…arameterizedTest`

See quarkusio#24492
  • Loading branch information
snazy committed Apr 11, 2022
1 parent 240bc51 commit cba33fa
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 3 deletions.
11 changes: 11 additions & 0 deletions docs/src/main/asciidoc/getting-started-testing.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1394,3 +1394,14 @@ For `@QuarkusIntegrationTest` tests that result in launcher the application as a
This can be used by `QuarkusTestResourceLifecycleManager` that need to launch additional containers that the application will communicate with.
====

== Troubleshooting

=== Junit 5 parameterized tests failing on Java 16 and newer

`@ParameterizedTest`s in JUnit 5 can fail with a `java.lang.IllegalStateException` (or
`com.thoughtworks.xstream.converters.ConversionException` in older Quarkus versions) with a message `No converter available`
for new-ish collection types like `java.util.Arrays$ArrayList` or other "JDK internal" collection types like
`java.util.ImmutableCollections$List12`. The only workaround at this point is to add necessary `--add-opens` options.

Since `quarkus-junit5` uses [X-stream](https://github.com/x-stream/xstream) under the covers, there's no immediate
solution available, unfortunately.
17 changes: 17 additions & 0 deletions integration-tests/test-extension/tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,23 @@
<quarkus.package.type>uber-jar</quarkus.package.type>
</properties>
</profile>
<profile>
<id>jdk17</id>
<activation>
<jdk>[17,)</jdk>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>--add-opens java.base/java.util=ALL-UNNAMED</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package io.quarkus.it.extension;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;

import io.quarkus.test.junit.QuarkusTest;

/**
* Exercise {@link ParameterizedTest @ParameterizedTest}s.
*
* <p>
* This test will run into <a href="https://github.com/x-stream/xstream/issues/253">x-stream/xstream#253</a> on Java 16 and
* newer without the necessary {@code --add-opens} options.
*/
@QuarkusTest
public class ParamsTest {
@ParameterizedTest
@ValueSource(booleans = { true, false })
public void valuesBooleans(boolean ignore) {
}

@ParameterizedTest
@ValueSource(strings = { "true", "false" })
public void valuesStrings(String ignore) {
}

@ParameterizedTest
@ValueSource(classes = { String.class, TestData.class })
public void valuesClasses(Class<?> ignore) {
}

@ParameterizedTest
@ValueSource(chars = { 'a', 'b', 'c' })
public void valuesChars(char ignore) {
}

@ParameterizedTest
@ValueSource(bytes = { (byte) 1, (byte) 2, (byte) 3 })
public void valuesBytes(byte ignore) {
}

@ParameterizedTest
@MethodSource("testDataStreamList")
public void methodStreamList(List<String> ignore) {
}

static Stream<List<String>> testDataStreamList() {
return Stream.of(Arrays.asList("a"), Arrays.asList("b"));
}

@ParameterizedTest
@MethodSource("testDataStreamListOf")
public void methodStreamListOf(List<String> ignore) {
}

static Stream<List<String>> testDataStreamListOf() {
return Stream.of(List.of("a"));
}

@ParameterizedTest
@MethodSource("testDataStreamSetOf")
public void methodStreamListOf(Set<String> ignore) {
}

static Stream<Set<String>> testDataStreamSetOf() {
return Stream.of(Set.of("a"));
}

@ParameterizedTest
@MethodSource("testDataStreamArrayList")
public void methodStreamArrayList(List<String> ignore) {
}

static Stream<List<String>> testDataStreamArrayList() {
return Stream.of(Collections.emptyList());
}

@ParameterizedTest
@MethodSource("testDataStream")
public void methodStream(TestData ignore) {
}

static Stream<TestData> testDataStream() {
return Stream.of(new TestData());
}

@ParameterizedTest
@MethodSource("testDataList")
public void methodList(TestData ignore) {
}

static List<TestData> testDataList() {
return List.of(new TestData());
}

@ParameterizedTest
@MethodSource("testDataStreamArguments")
public void methodList(TestData ignore, String ignored) {
}

static Stream<Arguments> testDataStreamArguments() {
return Stream.of(Arguments.of(new TestData(), "foo"));
}

@ParameterizedTest
@MethodSource("testDataListArguments")
public void methodListArguments(TestData ignore, String ignored) {
}

static List<Arguments> testDataListArguments() {
return Arrays.asList(Arguments.of(new TestData(), "foo"), Arguments.of(new TestData(), "foo"));
}

@SuppressWarnings("unused")
static class TestData {
final List<String> foo = Arrays.asList("one", "two", "three");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
import java.util.function.Supplier;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.ConversionException;

/**
* Super simple cloning strategy that just serializes to XML and deserializes it using xstream
*/
class XStreamDeepClone implements DeepClone {

static final String DEFAULT_ACTION = "Please report the issue on the Quarkus issue tracker.";

private final Supplier<XStream> xStreamSupplier;

XStreamDeepClone(ClassLoader classLoader) {
Expand Down Expand Up @@ -46,12 +49,29 @@ public Object get() {

private Object doClone(Object objectToClone) {
XStream xStream = xStreamSupplier.get();
final String serialized = xStream.toXML(objectToClone);
final String serialized = serialize(objectToClone, xStream);
final Object result = xStream.fromXML(serialized);
if (result == null) {
throw new IllegalStateException("Unable to deep clone object of type '" + objectToClone.getClass().getName()
+ "'. Please report the issue on the Quarkus issue tracker.");
throw new IllegalStateException(cannotDeepCloneMessage(objectToClone, DEFAULT_ACTION));
}
return result;
}

private String serialize(Object objectToClone, XStream xStream) {
try {
return xStream.toXML(objectToClone);
} catch (ConversionException e) {
String additional = e.getMessage().startsWith("No converter available")
? "'No converter available' messages are known to occur with new-ish Java versions due " +
"to https://github.com/x-stream/xstream/issues/253, please try with JVM options proposed in " +
"the message of the nested ConversionException, " +
"if any, or report the issue on the Quarkus issue tracker.\n" + e.getMessage()
: DEFAULT_ACTION;
throw new IllegalStateException(cannotDeepCloneMessage(objectToClone, additional), e);
}
}

private String cannotDeepCloneMessage(Object objectToClone, String additional) {
return String.format("Unable to deep clone object of type '%s'. %s", objectToClone.getClass().getName(), additional);
}
}

0 comments on commit cba33fa

Please sign in to comment.