diff --git a/core/src/main/java/com/tngtech/junit/dataprovider/placeholder/AbstractArgumentPlaceholder.java b/core/src/main/java/com/tngtech/junit/dataprovider/placeholder/AbstractArgumentPlaceholder.java index 6bbe7b62..cdf812a6 100644 --- a/core/src/main/java/com/tngtech/junit/dataprovider/placeholder/AbstractArgumentPlaceholder.java +++ b/core/src/main/java/com/tngtech/junit/dataprovider/placeholder/AbstractArgumentPlaceholder.java @@ -30,7 +30,7 @@ * * */ -abstract class AbstractArgumentPlaceholder extends BasePlaceholder { +public abstract class AbstractArgumentPlaceholder extends BasePlaceholder { /** * {@link String} representation of {@code null} @@ -47,7 +47,7 @@ abstract class AbstractArgumentPlaceholder extends BasePlaceholder { */ protected static final String STRING_NON_PRINTABLE = ""; - AbstractArgumentPlaceholder(String placeholderRegex) { + protected AbstractArgumentPlaceholder(String placeholderRegex) { super(placeholderRegex); } diff --git a/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/AnnotationArgumentPlaceholder.java b/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/AnnotationArgumentPlaceholder.java new file mode 100644 index 00000000..895928f3 --- /dev/null +++ b/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/AnnotationArgumentPlaceholder.java @@ -0,0 +1,90 @@ +package com.tngtech.test.junit.dataprovider.custom.argformat; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Logger; + +import com.tngtech.junit.dataprovider.placeholder.AbstractArgumentPlaceholder; +import com.tngtech.junit.dataprovider.placeholder.NamedArgumentPlaceholder; +import com.tngtech.junit.dataprovider.placeholder.ReplacementData; + +class AnnotationArgumentPlaceholder extends AbstractArgumentPlaceholder { + + private static final Logger logger = Logger.getLogger(NamedArgumentPlaceholder.class.getName()); + + public AnnotationArgumentPlaceholder() { + super("%aa\\[(-?[0-9]+|-?[0-9]+\\.\\.-?[0-9]+)\\]"); + } + + @Override + protected String getReplacementFor(String placeholder, ReplacementData data) { + String subscript = placeholder.substring(4, placeholder.length() - 1); + + int from = Integer.MAX_VALUE; + int to = Integer.MIN_VALUE; + if (subscript.contains("..")) { + String[] split = subscript.split("\\.\\."); + + from = Integer.parseInt(split[0]); + to = Integer.parseInt(split[1]); + } else { + from = Integer.parseInt(subscript); + to = from; + } + + data.getTestMethod().getAnnotatedParameterTypes(); + + List arguments = data.getArguments(); + from = (from >= 0) ? from : arguments.size() + from; + to = (to >= 0) ? to + 1 : arguments.size() + to + 1; + return formatAll(getSubArrayOfMethodParameters(data.getTestMethod(), from, to), + arguments.subList(from, to)); + } + + /** + * Formats the given parameters and arguments to a comma-separated list of {@code $parameterName=$argumentName}. + * Arguments {@link String} representation are therefore treated specially. + * + * @param parameters used to for formatting + * @param arguments to be formatted + * @return the formatted {@link String} of the given {@link Parameter}{@code []} and {@link List}{@code } + */ + protected String formatAll(Parameter[] parameters, List arguments) { + StringBuilder stringBuilder = new StringBuilder(); + for (int idx = 0; idx < arguments.size(); idx++) { + Parameter parameter = parameters[idx]; + Object argument = arguments.get(idx); + + Format annotation = parameter.getAnnotation(Format.class); + if (annotation != null) { + try { + stringBuilder.append(annotation.value().newInstance().format(argument)); + + } catch (InstantiationException | IllegalAccessException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } else { + stringBuilder.append(format(argument)); + } + + if (idx < arguments.size() - 1) { + stringBuilder.append(", "); + } + } + return stringBuilder.toString(); + } + + private Parameter[] getSubArrayOfMethodParameters(Method testMethod, int fromIndex, int toIndex) { + Parameter[] parameters = testMethod.getParameters(); + if (parameters.length > 0 && !parameters[0].isNamePresent()) { + logger.warning(String.format("Parameter names on method '%s' are not available" + + ". To store formal parameter names, compile the source file with the '-parameters' option" + + ". See also https://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html", + testMethod)); + } + return Arrays.copyOfRange(parameters, fromIndex, toIndex); + } +} \ No newline at end of file diff --git a/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/AnnotationArgumentPlaceholderAcceptanceTest.java b/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/AnnotationArgumentPlaceholderAcceptanceTest.java new file mode 100644 index 00000000..974e701c --- /dev/null +++ b/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/AnnotationArgumentPlaceholderAcceptanceTest.java @@ -0,0 +1,26 @@ +package com.tngtech.test.junit.dataprovider.custom.argformat; + +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; + +import com.tngtech.junit.dataprovider.DataProvider; +import com.tngtech.junit.dataprovider.UseDataProvider; + +@ExtendWith(AnnotationArgumentPlaceholderDataProviderExtension.class) +class AnnotationArgumentPlaceholderAcceptanceTest { + + @DataProvider(format = "[%i: %aa[0..-1]]") + static Object[][] dataProvider() { + // @formatter:off + return new Object[][] { + { new WrappedClass(AnnotationArgumentPlaceholderAcceptanceTest.class) }, + }; + // @formatter:on + } + + @TestTemplate + @UseDataProvider + void test(@Format(UnwrapFormatter.class) WrappedClass clazz) { + // TODO + } +} diff --git a/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/AnnotationArgumentPlaceholderDataProviderExtension.java b/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/AnnotationArgumentPlaceholderDataProviderExtension.java new file mode 100644 index 00000000..fe9cd30c --- /dev/null +++ b/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/AnnotationArgumentPlaceholderDataProviderExtension.java @@ -0,0 +1,48 @@ +package com.tngtech.test.junit.dataprovider.custom.argformat; + +import static java.util.Arrays.asList; + +import java.util.List; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.platform.commons.support.ReflectionSupport; + +import com.tngtech.junit.dataprovider.DataProvider; +import com.tngtech.junit.dataprovider.DisplayNameContext; +import com.tngtech.junit.dataprovider.UseDataProvider; +import com.tngtech.junit.dataprovider.UseDataProviderInvocationContextProvider; +import com.tngtech.junit.dataprovider.convert.ConverterContext; +import com.tngtech.junit.dataprovider.placeholder.BasePlaceholder; +import com.tngtech.junit.dataprovider.resolver.DataProviderResolverContext; + +class AnnotationArgumentPlaceholderDataProviderExtension + extends UseDataProviderInvocationContextProvider { + + AnnotationArgumentPlaceholderDataProviderExtension() { + super(UseDataProvider.class, DataProvider.class); + } + + @Override + protected DataProviderResolverContext getDataProviderResolverContext(ExtensionContext extensionContext, + UseDataProvider testAnnotation) { + return new DataProviderResolverContext(extensionContext.getRequiredTestMethod(), + asList(testAnnotation.resolver()), testAnnotation.resolveStrategy(), asList(testAnnotation.location()), + DataProvider.class, testAnnotation.value()); + } + + @Override + protected ConverterContext getConverterContext(DataProvider dataProvider) { + return new ConverterContext(ReflectionSupport.newInstance(dataProvider.objectArrayConverter()), + ReflectionSupport.newInstance(dataProvider.singleArgConverter()), + ReflectionSupport.newInstance(dataProvider.stringConverter()), dataProvider.splitBy(), + dataProvider.convertNulls(), dataProvider.trimValues(), dataProvider.ignoreEnumCase()); + } + + @Override + protected DisplayNameContext getDisplayNameContext(DataProvider dataProvider) { + @SuppressWarnings("unchecked") + List defaultPlaceholders = (List) getDefaultPlaceholders(); + defaultPlaceholders.add(0, new AnnotationArgumentPlaceholder()); + return new DisplayNameContext(dataProvider.format(), defaultPlaceholders); + } +} diff --git a/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/ArgumentFormatter.java b/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/ArgumentFormatter.java new file mode 100644 index 00000000..7deef7a1 --- /dev/null +++ b/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/ArgumentFormatter.java @@ -0,0 +1,6 @@ +package com.tngtech.test.junit.dataprovider.custom.argformat; + +public interface ArgumentFormatter { + + String format(T argument); +} diff --git a/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/Format.java b/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/Format.java new file mode 100644 index 00000000..96c8dffd --- /dev/null +++ b/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/Format.java @@ -0,0 +1,11 @@ +package com.tngtech.test.junit.dataprovider.custom.argformat; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Format { + + Class value(); + +} diff --git a/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/UnwrapFormatter.java b/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/UnwrapFormatter.java new file mode 100644 index 00000000..8a2fbbba --- /dev/null +++ b/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/UnwrapFormatter.java @@ -0,0 +1,10 @@ +package com.tngtech.test.junit.dataprovider.custom.argformat; + +public class UnwrapFormatter implements ArgumentFormatter { + + @Override + public String format(WrappedClass argument) { + return argument.getWrappedClazz().getSimpleName(); + } + +} diff --git a/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/WrappedClass.java b/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/WrappedClass.java new file mode 100644 index 00000000..a8f3f52c --- /dev/null +++ b/junit-jupiter/src/integTest/java/com/tngtech/test/junit/dataprovider/custom/argformat/WrappedClass.java @@ -0,0 +1,18 @@ +package com.tngtech.test.junit.dataprovider.custom.argformat; + +class WrappedClass { + private final Class clazz; + + WrappedClass(Class clazz) { + this.clazz = clazz; + } + + public Class getWrappedClazz() { + return clazz; + } + + @Override + public String toString() { + return "WrappedClass [clazz=" + clazz + "]"; + } +} \ No newline at end of file