Skip to content

Commit c68dd38

Browse files
committed
Extract preliminary reusable Condition API from LifecycleMethodUtils
1 parent 49acd3f commit c68dd38

File tree

4 files changed

+107
-107
lines changed

4 files changed

+107
-107
lines changed

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/LifecycleMethodUtils.java

+12-71
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,24 @@
1212

1313
import static org.junit.platform.commons.support.AnnotationSupport.findAnnotatedMethods;
1414
import static org.junit.platform.commons.util.CollectionUtils.toUnmodifiableList;
15+
import static org.junit.platform.engine.support.discovery.DiscoveryIssueReporter.Condition.allOf;
1516

1617
import java.lang.annotation.Annotation;
1718
import java.lang.reflect.Method;
1819
import java.util.List;
19-
import java.util.function.Consumer;
20-
import java.util.function.Function;
21-
import java.util.function.Predicate;
2220

2321
import org.junit.jupiter.api.AfterAll;
2422
import org.junit.jupiter.api.AfterEach;
2523
import org.junit.jupiter.api.BeforeAll;
2624
import org.junit.jupiter.api.BeforeEach;
2725
import org.junit.platform.commons.support.HierarchyTraversalMode;
2826
import org.junit.platform.commons.support.ModifierSupport;
29-
import org.junit.platform.commons.util.Preconditions;
3027
import org.junit.platform.commons.util.ReflectionUtils;
3128
import org.junit.platform.engine.DiscoveryIssue;
3229
import org.junit.platform.engine.DiscoveryIssue.Severity;
3330
import org.junit.platform.engine.support.descriptor.MethodSource;
3431
import org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;
32+
import org.junit.platform.engine.support.discovery.DiscoveryIssueReporter.Condition;
3533

3634
/**
3735
* Collection of utilities for working with test lifecycle methods.
@@ -70,7 +68,7 @@ private static List<Method> findMethodsAndCheckStatic(Class<?> testClass, boolea
7068
Class<? extends Annotation> annotationType, HierarchyTraversalMode traversalMode,
7169
DiscoveryIssueReporter issueReporter) {
7270

73-
Predicate<Method> additionalCondition = requireStatic ? isStatic(annotationType, issueReporter) : __ -> true;
71+
Condition<Method> additionalCondition = requireStatic ? isStatic(annotationType, issueReporter) : __ -> true;
7472
return findMethodsAndCheckVoidReturnType(testClass, annotationType, traversalMode, issueReporter,
7573
additionalCondition);
7674
}
@@ -85,46 +83,46 @@ private static List<Method> findMethodsAndCheckNonStatic(Class<?> testClass,
8583

8684
private static List<Method> findMethodsAndCheckVoidReturnType(Class<?> testClass,
8785
Class<? extends Annotation> annotationType, HierarchyTraversalMode traversalMode,
88-
DiscoveryIssueReporter issueReporter, Predicate<? super Method> additionalCondition) {
86+
DiscoveryIssueReporter issueReporter, Condition<? super Method> additionalCondition) {
8987

9088
return findAnnotatedMethods(testClass, annotationType, traversalMode).stream() //
9189
.peek(isNotPrivate(annotationType, issueReporter)) //
9290
.filter(allOf(returnsPrimitiveVoid(annotationType, issueReporter), additionalCondition)) //
9391
.collect(toUnmodifiableList());
9492
}
9593

96-
private static Predicate<Method> isStatic(Class<? extends Annotation> annotationType,
94+
private static Condition<Method> isStatic(Class<? extends Annotation> annotationType,
9795
DiscoveryIssueReporter issueReporter) {
98-
return DiscoveryIssueReportingPredicate.from(ModifierSupport::isStatic, issueReporter, method -> {
96+
return issueReporter.createReportingCondition(ModifierSupport::isStatic, method -> {
9997
String message = String.format(
10098
"@%s method '%s' must be static unless the test class is annotated with @TestInstance(Lifecycle.PER_CLASS).",
10199
annotationType.getSimpleName(), method.toGenericString());
102100
return createIssue(Severity.ERROR, message, method);
103101
});
104102
}
105103

106-
private static Predicate<Method> isNotStatic(Class<? extends Annotation> annotationType,
104+
private static Condition<Method> isNotStatic(Class<? extends Annotation> annotationType,
107105
DiscoveryIssueReporter issueReporter) {
108-
return DiscoveryIssueReportingPredicate.from(ModifierSupport::isNotStatic, issueReporter, method -> {
106+
return issueReporter.createReportingCondition(ModifierSupport::isNotStatic, method -> {
109107
String message = String.format("@%s method '%s' must not be static.", annotationType.getSimpleName(),
110108
method.toGenericString());
111109
return createIssue(Severity.ERROR, message, method);
112110
});
113111
}
114112

115-
private static Consumer<Method> isNotPrivate(Class<? extends Annotation> annotationType,
113+
private static Condition<Method> isNotPrivate(Class<? extends Annotation> annotationType,
116114
DiscoveryIssueReporter issueReporter) {
117-
return DiscoveryIssueReportingPredicate.from(ModifierSupport::isNotPrivate, issueReporter, method -> {
115+
return issueReporter.createReportingCondition(ModifierSupport::isNotPrivate, method -> {
118116
String message = String.format(
119117
"@%s method '%s' should not be private. This will be disallowed in a future release.",
120118
annotationType.getSimpleName(), method.toGenericString());
121119
return createIssue(Severity.DEPRECATION, message, method);
122120
});
123121
}
124122

125-
private static Predicate<Method> returnsPrimitiveVoid(Class<? extends Annotation> annotationType,
123+
private static Condition<Method> returnsPrimitiveVoid(Class<? extends Annotation> annotationType,
126124
DiscoveryIssueReporter issueReporter) {
127-
return DiscoveryIssueReportingPredicate.from(ReflectionUtils::returnsPrimitiveVoid, issueReporter, method -> {
125+
return issueReporter.createReportingCondition(ReflectionUtils::returnsPrimitiveVoid, method -> {
128126
String message = String.format("@%s method '%s' must not return a value.", annotationType.getSimpleName(),
129127
method.toGenericString());
130128
return createIssue(Severity.ERROR, message, method);
@@ -135,61 +133,4 @@ private static DiscoveryIssue createIssue(Severity severity, String message, Met
135133
return DiscoveryIssue.builder(severity, message).source(MethodSource.from(method)).build();
136134
}
137135

138-
@SafeVarargs
139-
@SuppressWarnings("varargs")
140-
private static <T> Predicate<T> allOf(Predicate<? super T>... predicates) {
141-
Preconditions.notNull(predicates, "predicates must not be null");
142-
Preconditions.notEmpty(predicates, "predicates must not be empty");
143-
Preconditions.containsNoNullElements(predicates, "predicates must not contain null elements");
144-
return value -> {
145-
boolean result = true;
146-
for (Predicate<? super T> predicate : predicates) {
147-
result &= predicate.test(value);
148-
}
149-
return result;
150-
};
151-
}
152-
153-
private abstract static class DiscoveryIssueReportingPredicate<T> implements Predicate<T>, Consumer<T> {
154-
155-
static <T> DiscoveryIssueReportingPredicate<T> from(Predicate<T> predicate, DiscoveryIssueReporter reporter,
156-
Function<T, DiscoveryIssue> issueBuilder) {
157-
return new DiscoveryIssueReportingPredicate<T>(reporter) {
158-
@Override
159-
protected boolean checkCondition(T value) {
160-
return predicate.test(value);
161-
}
162-
163-
@Override
164-
protected DiscoveryIssue createIssue(T value) {
165-
return issueBuilder.apply(value);
166-
}
167-
};
168-
}
169-
170-
private final DiscoveryIssueReporter reporter;
171-
172-
protected DiscoveryIssueReportingPredicate(DiscoveryIssueReporter reporter) {
173-
this.reporter = reporter;
174-
}
175-
176-
@Override
177-
public final boolean test(T value) {
178-
if (checkCondition(value)) {
179-
return true;
180-
}
181-
reporter.reportIssue(createIssue(value));
182-
return false;
183-
}
184-
185-
@Override
186-
public void accept(T value) {
187-
test(value);
188-
}
189-
190-
protected abstract boolean checkCondition(T value);
191-
192-
protected abstract DiscoveryIssue createIssue(T value);
193-
}
194-
195136
}

junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/DefaultDiscoveryIssueReporter.java

-35
This file was deleted.

junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/DiscoveryIssueReporter.java

+94
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,15 @@
1212

1313
import static org.apiguardian.api.API.Status.EXPERIMENTAL;
1414

15+
import java.util.function.Consumer;
16+
import java.util.function.Function;
17+
import java.util.function.Predicate;
18+
1519
import org.apiguardian.api.API;
20+
import org.junit.platform.commons.util.Preconditions;
1621
import org.junit.platform.engine.DiscoveryIssue;
22+
import org.junit.platform.engine.EngineDiscoveryListener;
23+
import org.junit.platform.engine.UniqueId;
1724

1825
/**
1926
* {@code DiscoveryIssueReporter} defines the API for reporting
@@ -26,6 +33,20 @@
2633
@FunctionalInterface
2734
public interface DiscoveryIssueReporter {
2835

36+
/**
37+
* Create a new {@code DiscoveryIssueReporter} that reports issues to the
38+
* supplied {@link EngineDiscoveryListener} for the specified engine.
39+
*
40+
* @param engineDiscoveryListener the listener to report issues to; never
41+
* {@code null}
42+
* @param engineId the unique identifier of the engine; never {@code null}
43+
*/
44+
static DiscoveryIssueReporter create(EngineDiscoveryListener engineDiscoveryListener, UniqueId engineId) {
45+
Preconditions.notNull(engineDiscoveryListener, "engineDiscoveryListener must not be null");
46+
Preconditions.notNull(engineId, "engineId must not be null");
47+
return issue -> engineDiscoveryListener.issueEncountered(engineId, issue);
48+
}
49+
2950
/**
3051
* Build the supplied {@link DiscoveryIssue.Builder Builder} and report the
3152
* resulting {@link DiscoveryIssue}.
@@ -39,4 +60,77 @@ default void reportIssue(DiscoveryIssue.Builder builder) {
3960
*/
4061
void reportIssue(DiscoveryIssue issue);
4162

63+
/**
64+
* Create a {@link Condition} that reports a {@link DiscoveryIssue} when the
65+
* supplied {@link Predicate} is not met.
66+
*
67+
* @param predicate the predicate to test; never {@code null}
68+
* @param issueCreator the function to create the issue with; never {@code null}
69+
* @return a new {@code Condition}; never {@code null}
70+
*/
71+
default <T> Condition<T> createReportingCondition(Predicate<T> predicate,
72+
Function<T, DiscoveryIssue> issueCreator) {
73+
Preconditions.notNull(predicate, "predicate must not be null");
74+
Preconditions.notNull(issueCreator, "issueCreator must not be null");
75+
return value -> {
76+
if (predicate.test(value)) {
77+
return true;
78+
}
79+
else {
80+
reportIssue(issueCreator.apply(value));
81+
return false;
82+
}
83+
};
84+
}
85+
86+
/**
87+
* A {@code Condition} is a union of {@link Predicate} and {@link Consumer}.
88+
*
89+
* <p>Instances of this type may be used as {@link Predicate Predicates} or
90+
* {@link Consumer Consumers}. For example, a {@code Condition} may be
91+
* passed to {@link java.util.stream.Stream#filter(Predicate)} if it is used
92+
* for filtering, or to {@link java.util.stream.Stream#peek(Consumer)} if it
93+
* is only used for reporting or other side effects.
94+
*
95+
* @see #createReportingCondition(Predicate, Function)
96+
*/
97+
@FunctionalInterface
98+
interface Condition<T> extends Predicate<T>, Consumer<T> {
99+
100+
/**
101+
* Return a composed condition that represents a logical AND of the
102+
* supplied conditions without short-circuiting.
103+
*
104+
* <p><em>All</em> of the supplied conditions will be evaluated even if
105+
* one or more of them return {@code false} to ensure that all issues
106+
* are reported.
107+
*
108+
* @param conditions the conditions to compose; never {@code null}, not
109+
* empty, and must not contain any {@code null} elements
110+
* @return the composed condition; never {@code null}
111+
*/
112+
@SafeVarargs
113+
@SuppressWarnings("varargs")
114+
static <T> Condition<T> allOf(Condition<? super T>... conditions) {
115+
Preconditions.notNull(conditions, "conditions must not be null");
116+
Preconditions.notEmpty(conditions, "conditions must not be empty");
117+
Preconditions.containsNoNullElements(conditions, "conditions must not contain null elements");
118+
return value -> {
119+
boolean result = true;
120+
for (Condition<? super T> condition : conditions) {
121+
result &= condition.test(value);
122+
}
123+
return result;
124+
};
125+
}
126+
127+
/**
128+
* Evaluate the {@code #test(Object)} method of this condition to
129+
* potentially report an issue.
130+
*/
131+
@Override
132+
default void accept(T value) {
133+
test(value);
134+
}
135+
}
42136
}

junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/EngineDiscoveryRequestResolver.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ private EngineDiscoveryRequestResolver(List<Function<InitializationContext<T>, S
110110
public void resolve(EngineDiscoveryRequest request, T engineDescriptor) {
111111
Preconditions.notNull(request, "request must not be null");
112112
Preconditions.notNull(engineDescriptor, "engineDescriptor must not be null");
113-
DiscoveryIssueReporter issueReporter = new DefaultDiscoveryIssueReporter(request.getDiscoveryListener(),
113+
DiscoveryIssueReporter issueReporter = DiscoveryIssueReporter.create(request.getDiscoveryListener(),
114114
engineDescriptor.getUniqueId());
115115
InitializationContext<T> initializationContext = new DefaultInitializationContext<>(request, engineDescriptor,
116116
issueReporter);

0 commit comments

Comments
 (0)