Skip to content

Commit

Permalink
Introduce named argument set support for @⁠ParameterizedTest
Browse files Browse the repository at this point in the history
Running ArgumentSetDemo, results in the following output:

[1] Important Files
[2] Other Files

Important Files :: path1, path2
Other Files :: path3, path4

[1] Mixed Arguments Types :: Important Files
[2] Mixed Arguments Types :: file1=path3, file2=path4

Closes #3818
  • Loading branch information
sbrannen committed May 20, 2024
1 parent f82aefa commit 4a0a45b
Show file tree
Hide file tree
Showing 7 changed files with 360 additions and 71 deletions.
67 changes: 67 additions & 0 deletions documentation/src/test/java/example/ArgumentSetDemo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright 2015-2024 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
* accompanies this distribution and is available at
*
* https://www.eclipse.org/legal/epl-v20.html
*/

package example;

import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD;
import static org.junit.jupiter.params.provider.Arguments.argumentSet;
import static org.junit.jupiter.params.provider.Arguments.arguments;

import java.io.File;
import java.util.Arrays;
import java.util.List;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.FieldSource;

@Execution(SAME_THREAD)
class ArgumentSetDemo {

@BeforeEach
void printDisplayName(TestInfo testInfo) {
System.out.println(testInfo.getDisplayName());
}

@ParameterizedTest
@FieldSource("argumentSets")
void defaultArgumentSetDisplayName(File file1, File file2) {
}

@ParameterizedTest(name = "{argumentSetName} :: {arguments}")
@FieldSource("argumentSets")
void customArgumentSetNameAndArgumentsDisplayName(File file1, File file2) {
}

@DisplayName("Mixed Arguments Types")
@ParameterizedTest(name = "[{index}] {displayName} :: {argumentSetNameOrArgumentsWithNames}")
@FieldSource("mixedArgumentsTypes")
void mixedArgumentsTypesDisplayName(File file1, File file2) {
}

// @formatter:off
static List<Arguments> argumentSets = Arrays.asList(
argumentSet("Important Files", new File("path1"), new File("path2")),
argumentSet("Other Files", new File("path3"), new File("path4"))
);
// @formatter:on

// @formatter:off
static List<Arguments> mixedArgumentsTypes = Arrays.asList(
argumentSet("Important Files", new File("path1"), new File("path2")),
arguments(new File("path3"), new File("path4"))
);
// @formatter:on

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

package org.junit.jupiter.params;

import static org.apiguardian.api.API.Status.EXPERIMENTAL;
import static org.apiguardian.api.API.Status.STABLE;

import java.lang.annotation.Documented;
Expand Down Expand Up @@ -148,6 +149,7 @@
*
* @since 5.3
* @see #name
* @see #DEFAULT_DISPLAY_NAME
*/
String INDEX_PLACEHOLDER = "{index}";

Expand All @@ -173,9 +175,43 @@
*
* @since 5.6
* @see #name
* @see #ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER
*/
String ARGUMENTS_WITH_NAMES_PLACEHOLDER = "{argumentsWithNames}";

/**
* Placeholder for the name of the argument set for the current invocation
* of a {@code @ParameterizedTest} method: <code>{argumentSetName}</code>.
*
* <p>This placeholder can be used when the current set of arguments was created via
* {@link org.junit.jupiter.params.provider.Arguments#argumentSet(String, Object...)
* argumentSet()}.
*
* @since 5.11
* @see #name
* @see #ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER
* @see org.junit.jupiter.params.provider.Arguments#argumentSet(String, Object...)
*/
@API(status = EXPERIMENTAL, since = "5.11")
String ARGUMENT_SET_NAME_PLACEHOLDER = "{argumentSetName}";

/**
* Placeholder for either {@link #ARGUMENT_SET_NAME_PLACEHOLDER} or
* {@link #ARGUMENTS_WITH_NAMES_PLACEHOLDER}, depending on whether the
* current set of arguments was created via
* {@link org.junit.jupiter.params.provider.Arguments#argumentSet(String, Object...)
* argumentSet()}: <code>{argumentSetNameOrArgumentsWithNames}</code>.
*
* @since 5.11
* @see #name
* @see #ARGUMENT_SET_NAME_PLACEHOLDER
* @see #ARGUMENTS_WITH_NAMES_PLACEHOLDER
* @see #DEFAULT_DISPLAY_NAME
* @see org.junit.jupiter.params.provider.Arguments#argumentSet(String, Object...)
*/
@API(status = EXPERIMENTAL, since = "5.11")
String ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER = "{argumentSetNameOrArgumentsWithNames}";

/**
* Default display name pattern for the current invocation of a
* {@code @ParameterizedTest} method: {@value}
Expand All @@ -188,9 +224,10 @@
* @see #name
* @see #DISPLAY_NAME_PLACEHOLDER
* @see #INDEX_PLACEHOLDER
* @see #ARGUMENTS_WITH_NAMES_PLACEHOLDER
* @see #ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER
*/
String DEFAULT_DISPLAY_NAME = "[" + INDEX_PLACEHOLDER + "] " + ARGUMENTS_WITH_NAMES_PLACEHOLDER;
String DEFAULT_DISPLAY_NAME = "[" + INDEX_PLACEHOLDER + "] "
+ ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER;

/**
* The display name to be used for individual invocations of the
Expand All @@ -215,8 +252,10 @@
* <ul>
* <li><code>{@value #DISPLAY_NAME_PLACEHOLDER}</code></li>
* <li><code>{@value #INDEX_PLACEHOLDER}</code></li>
* <li><code>{@value #ARGUMENT_SET_NAME_PLACEHOLDER}</code></li>
* <li><code>{@value #ARGUMENTS_PLACEHOLDER}</code></li>
* <li><code>{@value #ARGUMENTS_WITH_NAMES_PLACEHOLDER}</code></li>
* <li><code>{@value #ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER}</code></li>
* <li><code>"{0}"</code>, <code>"{1}"</code>, etc.: an individual argument (0-based)</li>
* </ul>
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import static org.junit.platform.commons.util.AnnotationUtils.isAnnotated;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Stream;

Expand Down Expand Up @@ -88,8 +87,6 @@ public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContex
.map(this::instantiateArgumentsProvider)
.map(provider -> AnnotationConsumerInitializer.initialize(templateMethod, provider))
.flatMap(provider -> arguments(provider, extensionContext))
.map(Arguments::get)
.map(arguments -> consumedArguments(arguments, methodContext))
.map(arguments -> {
invocationCount.incrementAndGet();
return createInvocationContext(formatter, methodContext, arguments, invocationCount.intValue());
Expand Down Expand Up @@ -122,7 +119,7 @@ private ExtensionContext.Store getStore(ExtensionContext context) {
}

private TestTemplateInvocationContext createInvocationContext(ParameterizedTestNameFormatter formatter,
ParameterizedTestMethodContext methodContext, Object[] arguments, int invocationIndex) {
ParameterizedTestMethodContext methodContext, Arguments arguments, int invocationIndex) {

return new ParameterizedTestInvocationContext(formatter, methodContext, arguments, invocationIndex);
}
Expand Down Expand Up @@ -151,12 +148,4 @@ protected static Stream<? extends Arguments> arguments(ArgumentsProvider provide
}
}

private Object[] consumedArguments(Object[] arguments, ParameterizedTestMethodContext methodContext) {
if (methodContext.hasAggregator()) {
return arguments;
}
int parameterCount = methodContext.getParameterCount();
return arguments.length > parameterCount ? Arrays.copyOf(arguments, parameterCount) : arguments;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@

import static java.util.Collections.singletonList;

import java.util.Arrays;
import java.util.List;

import org.junit.jupiter.api.extension.Extension;
import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
import org.junit.jupiter.params.provider.Arguments;

/**
* @since 5.0
Expand All @@ -24,27 +26,37 @@ class ParameterizedTestInvocationContext implements TestTemplateInvocationContex

private final ParameterizedTestNameFormatter formatter;
private final ParameterizedTestMethodContext methodContext;
private final Object[] arguments;
private final Arguments arguments;
private final Object[] consumedArguments;
private final int invocationIndex;

ParameterizedTestInvocationContext(ParameterizedTestNameFormatter formatter,
ParameterizedTestMethodContext methodContext, Object[] arguments, int invocationIndex) {
ParameterizedTestMethodContext methodContext, Arguments arguments, int invocationIndex) {

this.formatter = formatter;
this.methodContext = methodContext;
this.arguments = arguments;
this.consumedArguments = consumedArguments(methodContext, arguments.get());
this.invocationIndex = invocationIndex;
}

@Override
public String getDisplayName(int invocationIndex) {
return this.formatter.format(invocationIndex, this.arguments);
return this.formatter.format(invocationIndex, this.arguments, this.consumedArguments);
}

@Override
public List<Extension> getAdditionalExtensions() {
return singletonList(
new ParameterizedTestParameterResolver(this.methodContext, this.arguments, this.invocationIndex));
new ParameterizedTestParameterResolver(this.methodContext, this.consumedArguments, this.invocationIndex));
}

private static Object[] consumedArguments(ParameterizedTestMethodContext methodContext, Object[] arguments) {
if (methodContext.hasAggregator()) {
return arguments;
}
int parameterCount = methodContext.getParameterCount();
return arguments.length > parameterCount ? Arrays.copyOf(arguments, parameterCount) : arguments;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import static java.util.stream.Collectors.joining;
import static org.junit.jupiter.params.ParameterizedTest.ARGUMENTS_PLACEHOLDER;
import static org.junit.jupiter.params.ParameterizedTest.ARGUMENTS_WITH_NAMES_PLACEHOLDER;
import static org.junit.jupiter.params.ParameterizedTest.ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER;
import static org.junit.jupiter.params.ParameterizedTest.ARGUMENT_SET_NAME_PLACEHOLDER;
import static org.junit.jupiter.params.ParameterizedTest.DISPLAY_NAME_PLACEHOLDER;
import static org.junit.jupiter.params.ParameterizedTest.INDEX_PLACEHOLDER;

Expand All @@ -22,6 +24,9 @@
import java.util.stream.IntStream;

import org.junit.jupiter.api.Named;
import org.junit.jupiter.api.extension.ExtensionConfigurationException;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.Arguments.ArgumentSet;
import org.junit.platform.commons.JUnitException;
import org.junit.platform.commons.util.StringUtils;

Expand All @@ -47,9 +52,9 @@ class ParameterizedTestNameFormatter {
this.argumentMaxLength = argumentMaxLength;
}

String format(int invocationIndex, Object... arguments) {
String format(int invocationIndex, Arguments arguments, Object[] consumedArguments) {
try {
return formatSafely(invocationIndex, arguments);
return formatSafely(invocationIndex, arguments, consumedArguments);
}
catch (Exception ex) {
String message = "The display name pattern defined for the parameterized test is invalid. "
Expand All @@ -58,9 +63,9 @@ String format(int invocationIndex, Object... arguments) {
}
}

private String formatSafely(int invocationIndex, Object[] arguments) {
Object[] namedArguments = extractNamedArguments(arguments);
String pattern = prepareMessageFormatPattern(invocationIndex, namedArguments);
private String formatSafely(int invocationIndex, Arguments arguments, Object[] consumedArguments) {
Object[] namedArguments = extractNamedArguments(consumedArguments);
String pattern = prepareMessageFormatPattern(invocationIndex, arguments, namedArguments);
MessageFormat format = new MessageFormat(pattern);
Object[] humanReadableArguments = makeReadable(format, namedArguments);
String formatted = format.format(humanReadableArguments);
Expand All @@ -73,17 +78,34 @@ private Object[] extractNamedArguments(Object[] arguments) {
.toArray();
}

private String prepareMessageFormatPattern(int invocationIndex, Object[] arguments) {
private String prepareMessageFormatPattern(int invocationIndex, Arguments arguments, Object[] argumentsArray) {
String result = this.pattern//
.replace(DISPLAY_NAME_PLACEHOLDER, TEMPORARY_DISPLAY_NAME_PLACEHOLDER)//
.replace(INDEX_PLACEHOLDER, String.valueOf(invocationIndex));

if (result.contains(ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER)) {
String placeholderToUse = (arguments instanceof ArgumentSet //
? ARGUMENT_SET_NAME_PLACEHOLDER
: ARGUMENTS_WITH_NAMES_PLACEHOLDER);
result = result.replace(ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER, placeholderToUse);
}

if (result.contains(ARGUMENT_SET_NAME_PLACEHOLDER)) {
if (!(arguments instanceof ArgumentSet)) {
throw new ExtensionConfigurationException(
String.format("When the display name pattern for a @ParameterizedTest contains %s, "
+ "the arguments must be supplied as an ArgumentSet.",
ARGUMENT_SET_NAME_PLACEHOLDER));
}
result = result.replace(ARGUMENT_SET_NAME_PLACEHOLDER, ((ArgumentSet) arguments).getName());
}

if (result.contains(ARGUMENTS_WITH_NAMES_PLACEHOLDER)) {
result = result.replace(ARGUMENTS_WITH_NAMES_PLACEHOLDER, argumentsWithNamesPattern(arguments));
result = result.replace(ARGUMENTS_WITH_NAMES_PLACEHOLDER, argumentsWithNamesPattern(argumentsArray));
}

if (result.contains(ARGUMENTS_PLACEHOLDER)) {
result = result.replace(ARGUMENTS_PLACEHOLDER, argumentsPattern(arguments));
result = result.replace(ARGUMENTS_PLACEHOLDER, argumentsPattern(argumentsArray));
}

return result;
Expand Down
Loading

0 comments on commit 4a0a45b

Please sign in to comment.