Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow DisplayNameGenerator to access runtime enclosing type for @Nested #4162

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,41 @@ 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 +135,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 +282,19 @@ 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,14 +314,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));
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));
}
Comment on lines 122 to 126
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method and the one above for nested classes should no longer be used and therefore deleted.


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,12 @@ 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 @@ -82,8 +82,9 @@ else if (isNestedTestClass.test(testClass)) {
@Override
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()))));
return toResolution(
context.addToParent(() -> selectClass(selector.getEnclosingClasses()), parent -> Optional.of(
newNestedClassTestDescriptor(parent, selector.getEnclosingClasses(), selector.getNestedClass()))));
}
return unresolved();
}
Expand Down Expand Up @@ -127,6 +128,13 @@ 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,13 @@ 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 +201,13 @@ 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 @@ -206,8 +226,8 @@ private Optional<TestDescriptor> resolve(List<Class<?>> enclosingClasses, Class<
return Optional.empty();
}
return context.addToParent(() -> selectClass(enclosingClasses, testClass), //
parent -> Optional.of(
createTestDescriptor(createUniqueId(method, parent), testClass, method, configuration)));
parent -> Optional.of(createTestDescriptor(createUniqueId(method, parent), enclosingClasses, testClass,
method, configuration)));
}

private DiscoverySelector selectClass(List<Class<?>> enclosingClasses, Class<?> testClass) {
Expand Down Expand Up @@ -246,6 +266,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,23 @@
/*
* 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 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,14 @@
/*
* 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 org.junit.jupiter.api;

public class ScenarioOneTests extends AbstractBaseTests {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* 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 org.junit.jupiter.api;

public class ScenarioTwoTests extends AbstractBaseTests {
}
Loading