Skip to content

Commit

Permalink
DisplayNameGenerator cannot access runtime enclosing type for @nested
Browse files Browse the repository at this point in the history
…test class: junit-team#4130
  • Loading branch information
neronsoda committed Nov 27, 2024
1 parent ad0ef2e commit 4276dd4
Show file tree
Hide file tree
Showing 12 changed files with 147 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import static org.junit.platform.commons.support.ModifierSupport.isStatic;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;

Expand Down Expand Up @@ -89,7 +90,39 @@ public interface DisplayNameGenerator {
* @param nestedClass the class to generate a name for; never {@code null}
* @return the display name for the nested class; never blank
*/
String generateDisplayNameForNestedClass(Class<?> nestedClass);
default String generateDisplayNameForNestedClass(Class<?> nestedClass) {
throw new UnsupportedOperationException("Implement generateDisplayNameForNestedClass(List<Class<?>>, Class<?>) instead");
}

/**
* Generate a display name for the given {@link Nested @Nested} inner test class.
*
* <p>If it returns {@code null}, the default display name generator will be used instead.
*
* @param enclosingInstanceTypes concrete type of the enclosing instance for the nested class; never {@code null}
* @param nestedClass the class to generate a name for; never {@code null}
* @return the display name for the nested class; never blank
*/
default String generateDisplayNameForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> nestedClass) {
return generateDisplayNameForNestedClass(nestedClass);
}

/**
* Generate a display name for the given method.
*
* <p>If it returns {@code null}, the default display name generator will be used instead.
*
* @implNote The class instance supplied as {@code testClass} may differ from
* the class returned by {@code testMethod.getDeclaringClass()} &mdash; for
* example, when a test method is inherited from a superclass.
*
* @param testClass the class the test method is invoked on; never {@code null}
* @param testMethod method to generate a display name for; never {@code null}
* @return the display name for the test; never blank
*/
default String generateDisplayNameForMethod(Class<?> testClass, Method testMethod) {
throw new UnsupportedOperationException("Implement generateDisplayNameForMethod(List<Class<?>>, Class<?>, Method) instead");
}

/**
* Generate a display name for the given method.
Expand All @@ -100,11 +133,15 @@ public interface DisplayNameGenerator {
* the class returned by {@code testMethod.getDeclaringClass()} &mdash; for
* example, when a test method is inherited from a superclass.
*
* @param enclosingInstanceTypes concrete types of the enclosing instance for the nested class; never {@code null}
* @param testClass the class the test method is invoked on; never {@code null}
* @param testMethod method to generate a display name for; never {@code null}
* @return the display name for the test; never blank
*/
String generateDisplayNameForMethod(Class<?> testClass, Method testMethod);
default String generateDisplayNameForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
Method testMethod) {
return generateDisplayNameForMethod(testClass, testMethod);
}

/**
* Generate a string representation of the formal parameters of the supplied
Expand Down Expand Up @@ -243,17 +280,17 @@ public String generateDisplayNameForClass(Class<?> testClass) {
}

@Override
public String generateDisplayNameForNestedClass(Class<?> nestedClass) {
return getSentenceBeginning(nestedClass);
public String generateDisplayNameForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> nestedClass) {
return getSentenceBeginning(enclosingInstanceTypes, nestedClass);
}

@Override
public String generateDisplayNameForMethod(Class<?> testClass, Method testMethod) {
return getSentenceBeginning(testClass) + getFragmentSeparator(testClass)
+ getGeneratorFor(testClass).generateDisplayNameForMethod(testClass, testMethod);
public String generateDisplayNameForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass, Method testMethod) {
return getSentenceBeginning(enclosingInstanceTypes, testClass) + getFragmentSeparator(testClass)
+ getGeneratorFor(testClass).generateDisplayNameForMethod(enclosingInstanceTypes, testClass, testMethod);
}

private String getSentenceBeginning(Class<?> testClass) {
private String getSentenceBeginning(List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {
Class<?> enclosingClass = testClass.getEnclosingClass();
boolean topLevelTestClass = (enclosingClass == null || isStatic(testClass));
Optional<String> displayName = findAnnotation(testClass, DisplayName.class)//
Expand All @@ -273,17 +310,19 @@ private String getSentenceBeginning(Class<?> testClass) {
return generateDisplayNameForClass(testClass);
}

Class<?> enclosingInstanceType = enclosingInstanceTypes.remove(enclosingInstanceTypes.size() - 1);

// Only build prefix based on the enclosing class if the enclosing
// class is also configured to use the IndicativeSentences generator.
boolean buildPrefix = findDisplayNameGeneration(enclosingClass)//
boolean buildPrefix = findDisplayNameGeneration(enclosingInstanceType)//
.map(DisplayNameGeneration::value)//
.filter(IndicativeSentences.class::equals)//
.isPresent();

String prefix = (buildPrefix ? getSentenceBeginning(enclosingClass) + getFragmentSeparator(testClass) : "");
String prefix = (buildPrefix ? getSentenceBeginning(enclosingInstanceTypes, enclosingInstanceType) + getFragmentSeparator(testClass) : "");

return prefix + displayName.orElseGet(
() -> getGeneratorFor(testClass).generateDisplayNameForNestedClass(testClass));
() -> getGeneratorFor(testClass).generateDisplayNameForNestedClass(testClass));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
Expand Down Expand Up @@ -95,6 +96,12 @@ static String determineDisplayNameForMethod(Class<?> testClass, Method testMetho
createDisplayNameSupplierForMethod(testClass, testMethod, configuration));
}

static String determineDisplayNameForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass, Method testMethod,
JupiterConfiguration configuration) {
return determineDisplayName(testMethod,
createDisplayNameSupplierForMethod(enclosingInstanceTypes, testClass, testMethod, configuration));
}

static Supplier<String> createDisplayNameSupplierForClass(Class<?> testClass, JupiterConfiguration configuration) {
return createDisplayNameSupplier(testClass, configuration,
generator -> generator.generateDisplayNameForClass(testClass));
Expand All @@ -106,12 +113,24 @@ static Supplier<String> createDisplayNameSupplierForNestedClass(Class<?> testCla
generator -> generator.generateDisplayNameForNestedClass(testClass));
}

static Supplier<String> createDisplayNameSupplierForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
JupiterConfiguration configuration) {
return createDisplayNameSupplier(testClass, configuration,
generator -> generator.generateDisplayNameForNestedClass(enclosingInstanceTypes, testClass));
}

private static Supplier<String> createDisplayNameSupplierForMethod(Class<?> testClass, Method testMethod,
JupiterConfiguration configuration) {
return createDisplayNameSupplier(testClass, configuration,
generator -> generator.generateDisplayNameForMethod(testClass, testMethod));
}

private static Supplier<String> createDisplayNameSupplierForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass, Method testMethod,
JupiterConfiguration configuration) {
return createDisplayNameSupplier(testClass, configuration,
generator -> generator.generateDisplayNameForMethod(enclosingInstanceTypes, testClass, testMethod));
}

private static Supplier<String> createDisplayNameSupplier(Class<?> testClass, JupiterConfiguration configuration,
Function<DisplayNameGenerator, String> generatorFunction) {
return () -> findDisplayNameGenerator(testClass) //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ public abstract class MethodBasedTestDescriptor extends JupiterTestDescriptor im
configuration);
}

MethodBasedTestDescriptor(UniqueId uniqueId, List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
Method testMethod, JupiterConfiguration configuration) {
this(uniqueId, determineDisplayNameForMethod(enclosingInstanceTypes, testClass, testMethod, configuration),
testClass, testMethod, configuration);
}

MethodBasedTestDescriptor(UniqueId uniqueId, String displayName, Class<?> testClass, Method testMethod,
JupiterConfiguration configuration) {
super(uniqueId, displayName, MethodSource.from(testClass, testMethod), configuration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ public NestedClassTestDescriptor(UniqueId uniqueId, Class<?> testClass, JupiterC
super(uniqueId, testClass, createDisplayNameSupplierForNestedClass(testClass, configuration), configuration);
}

public NestedClassTestDescriptor(UniqueId uniqueId, List<Class<?>> enclosingInstanceTypes, Class<?> testClass, JupiterConfiguration configuration) {
super(uniqueId, testClass, createDisplayNameSupplierForNestedClass(enclosingInstanceTypes, testClass, configuration), configuration);
}

// --- TestDescriptor ------------------------------------------------------

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;
Expand Down Expand Up @@ -67,6 +68,11 @@ public TestFactoryTestDescriptor(UniqueId uniqueId, Class<?> testClass, Method t
super(uniqueId, testClass, testMethod, configuration);
}

public TestFactoryTestDescriptor(UniqueId uniqueId, List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
Method testMethod, JupiterConfiguration configuration) {
super(uniqueId, enclosingInstanceTypes, testClass, testMethod, configuration);
}

// --- Filterable ----------------------------------------------------------

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import static org.junit.platform.commons.util.CollectionUtils.forEachInReverseOrder;

import java.lang.reflect.Method;
import java.util.List;

import org.apiguardian.api.API;
import org.junit.jupiter.api.TestInstance.Lifecycle;
Expand Down Expand Up @@ -80,6 +81,12 @@ public TestMethodTestDescriptor(UniqueId uniqueId, Class<?> testClass, Method te
this.interceptorCall = defaultInterceptorCall;
}

public TestMethodTestDescriptor(UniqueId uniqueId, List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
Method testMethod, JupiterConfiguration configuration) {
super(uniqueId, enclosingInstanceTypes, testClass, testMethod, configuration);
this.interceptorCall = defaultInterceptorCall;
}

TestMethodTestDescriptor(UniqueId uniqueId, String displayName, Class<?> testClass, Method testMethod,
JupiterConfiguration configuration, ReflectiveInterceptorCall<Method, Void> interceptorCall) {
super(uniqueId, displayName, testClass, testMethod, configuration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ public TestTemplateTestDescriptor(UniqueId uniqueId, Class<?> testClass, Method
super(uniqueId, testClass, templateMethod, configuration);
}

public TestTemplateTestDescriptor(UniqueId uniqueId, List<Class<?>> enclosingInstanceTypes, Class<?> testClass,
Method templateMethod, JupiterConfiguration configuration) {
super(uniqueId, enclosingInstanceTypes, testClass, templateMethod, configuration);
}

// --- Filterable ----------------------------------------------------------

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ else if (isNestedTestClass.test(testClass)) {
public Resolution resolve(NestedClassSelector selector, Context context) {
if (isNestedTestClass.test(selector.getNestedClass())) {
return toResolution(context.addToParent(() -> selectClass(selector.getEnclosingClasses()),
parent -> Optional.of(newNestedClassTestDescriptor(parent, selector.getNestedClass()))));
parent -> Optional.of(newNestedClassTestDescriptor(parent, selector.getEnclosingClasses(), selector.getNestedClass()))));
}
return unresolved();
}
Expand Down Expand Up @@ -127,6 +127,12 @@ private NestedClassTestDescriptor newNestedClassTestDescriptor(TestDescriptor pa
configuration);
}

private NestedClassTestDescriptor newNestedClassTestDescriptor(TestDescriptor parent, List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {
return new NestedClassTestDescriptor(
parent.getUniqueId().append(NestedClassTestDescriptor.SEGMENT_TYPE, testClass.getSimpleName()), enclosingInstanceTypes, testClass,
configuration);
}

private Resolution toResolution(Optional<? extends ClassBasedTestDescriptor> testDescriptor) {
return testDescriptor.map(it -> {
Class<?> testClass = it.getTestClass();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,12 @@ protected TestDescriptor createTestDescriptor(UniqueId uniqueId, Class<?> testCl
JupiterConfiguration configuration) {
return new TestMethodTestDescriptor(uniqueId, testClass, method, configuration);
}

@Override
protected TestDescriptor createTestDescriptor(UniqueId uniqueId, List<Class<?>> enclosingInstanceTypes,
Class<?> testClass, Method method, JupiterConfiguration configuration) {
return new TestMethodTestDescriptor(uniqueId, enclosingInstanceTypes, testClass, method, configuration);
}
},

TEST_FACTORY(new IsTestFactoryMethod(), TestFactoryTestDescriptor.SEGMENT_TYPE,
Expand All @@ -179,6 +185,12 @@ protected TestDescriptor createTestDescriptor(UniqueId uniqueId, Class<?> testCl
JupiterConfiguration configuration) {
return new TestFactoryTestDescriptor(uniqueId, testClass, method, configuration);
}

@Override
protected TestDescriptor createTestDescriptor(UniqueId uniqueId, List<Class<?>> enclosingInstanceTypes,
Class<?> testClass, Method method, JupiterConfiguration configuration) {
return new TestFactoryTestDescriptor(uniqueId, enclosingInstanceTypes, testClass, method, configuration);
}
},

TEST_TEMPLATE(new IsTestTemplateMethod(), TestTemplateTestDescriptor.SEGMENT_TYPE,
Expand All @@ -188,6 +200,12 @@ protected TestDescriptor createTestDescriptor(UniqueId uniqueId, Class<?> testCl
JupiterConfiguration configuration) {
return new TestTemplateTestDescriptor(uniqueId, testClass, method, configuration);
}

@Override
protected TestDescriptor createTestDescriptor(UniqueId uniqueId, List<Class<?>> enclosingInstanceTypes,
Class<?> testClass, Method method, JupiterConfiguration configuration) {
return new TestTemplateTestDescriptor(uniqueId, enclosingInstanceTypes, testClass, method, configuration);
}
};

private final Predicate<Method> methodPredicate;
Expand All @@ -207,7 +225,7 @@ private Optional<TestDescriptor> resolve(List<Class<?>> enclosingClasses, Class<
}
return context.addToParent(() -> selectClass(enclosingClasses, testClass), //
parent -> Optional.of(
createTestDescriptor(createUniqueId(method, parent), testClass, method, configuration)));
createTestDescriptor(createUniqueId(method, parent), enclosingClasses, testClass, method, configuration)));
}

private DiscoverySelector selectClass(List<Class<?>> enclosingClasses, Class<?> testClass) {
Expand Down Expand Up @@ -246,6 +264,9 @@ private UniqueId createUniqueId(Method method, TestDescriptor parent) {
protected abstract TestDescriptor createTestDescriptor(UniqueId uniqueId, Class<?> testClass, Method method,
JupiterConfiguration configuration);

protected abstract TestDescriptor createTestDescriptor(UniqueId uniqueId, List<Class<?>> enclosingInstanceTypes,
Class<?> testClass, Method method, JupiterConfiguration configuration);

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.junit.jupiter.api;

@IndicativeSentencesGeneration
abstract class AbstractBaseTests {

@Nested
class NestedTests {

@Test
void test() {
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.junit.jupiter.api;

public class ScenarioOneTests extends AbstractBaseTests {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.junit.jupiter.api;

public class ScenarioTwoTests extends AbstractBaseTests {
}

0 comments on commit 4276dd4

Please sign in to comment.