{@code @TestFactory} methods must not be {@code private} or {@code static} * and must return a {@code Stream}, {@code Collection}, {@code Iterable}, - * {@code Iterator}, or array of {@link DynamicNode} instances. Supported + * {@code Iterator}, array of {@link DynamicNode} instances, or any type that + * provides an {@link java.util.Iterator Iterator}-returning {@code iterator()} + * method (such as, for example, a {@code kotlin.sequences.Sequence}). Supported * subclasses of {@code DynamicNode} include {@link DynamicContainer} and * {@link DynamicTest}. Dynamic tests will be executed lazily, * enabling dynamic and even non-deterministic generation of test cases. diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/AbstractJreCondition.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/AbstractJreCondition.java index 43ca0b92555d..1eb301b2d894 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/AbstractJreCondition.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/AbstractJreCondition.java @@ -10,6 +10,8 @@ package org.junit.jupiter.api.condition; +import static java.util.function.Predicate.isEqual; + import java.lang.annotation.Annotation; import java.util.Arrays; import java.util.function.Function; @@ -42,18 +44,15 @@ protected final IntStream validatedVersions(JRE[] jres, int[] versions) { Preconditions.condition(jres.length > 0 || versions.length > 0, () -> "You must declare at least one JRE or version in @" + this.annotationName); + Preconditions.condition(Arrays.stream(jres).noneMatch(isEqual(JRE.UNDEFINED)), + () -> "JRE.UNDEFINED is not supported in @" + this.annotationName); + Arrays.stream(versions).min().ifPresent(version -> Preconditions.condition(version >= JRE.MINIMUM_VERSION, + () -> String.format("Version [%d] in @%s must be greater than or equal to %d", version, this.annotationName, + JRE.MINIMUM_VERSION))); + return IntStream.concat(// - Arrays.stream(jres).mapToInt(jre -> { - Preconditions.condition(jre != JRE.UNDEFINED, - () -> "JRE.UNDEFINED is not supported in @" + this.annotationName); - return jre.version(); - }), // - Arrays.stream(versions).map(version -> { - Preconditions.condition(version >= JRE.MINIMUM_VERSION, - () -> String.format("Version [%d] in @%s must be greater than or equal to %d", version, - this.annotationName, JRE.MINIMUM_VERSION)); - return version; - })// + Arrays.stream(jres).mapToInt(JRE::version), // + Arrays.stream(versions) // ).distinct(); } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/AbstractJreRangeCondition.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/AbstractJreRangeCondition.java index ea2290e5c8de..7bc7d8a0b117 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/AbstractJreRangeCondition.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/AbstractJreRangeCondition.java @@ -70,9 +70,6 @@ protected final boolean isCurrentVersionWithinRange(JRE minJre, JRE maxJre, int // Finally, we need to validate the effective minimum and maximum values. Preconditions.condition((min != JRE.MINIMUM_VERSION || max != Integer.MAX_VALUE), () -> "You must declare a non-default value for the minimum or maximum value in @" + this.annotationName); - Preconditions.condition(min >= JRE.MINIMUM_VERSION, - () -> String.format("@%s's minimum value [%d] must greater than or equal to %d", this.annotationName, min, - JRE.MINIMUM_VERSION)); Preconditions.condition(min <= max, () -> String.format("@%s's minimum value [%d] must be less than or equal to its maximum value [%d]", this.annotationName, min, max)); diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionContext.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionContext.java index ae6daf29e2c3..1f1eeda903f4 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionContext.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionContext.java @@ -11,6 +11,7 @@ package org.junit.jupiter.api.extension; import static java.util.Collections.unmodifiableList; +import static org.apiguardian.api.API.Status.DEPRECATED; import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.INTERNAL; import static org.apiguardian.api.API.Status.STABLE; @@ -506,7 +507,7 @@ interface Store { * @deprecated Please extend {@code AutoCloseable} directly. */ @Deprecated - @API(status = STABLE, since = "5.1") + @API(status = DEPRECATED, since = "5.13") interface CloseableResource { /** diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestFactoryMethod.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestFactoryMethod.java index aaf90d6db727..144ec6db621c 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestFactoryMethod.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestFactoryMethod.java @@ -11,14 +11,13 @@ package org.junit.jupiter.engine.discovery.predicates; import static org.apiguardian.api.API.Status.INTERNAL; +import static org.junit.platform.commons.util.CollectionUtils.isConvertibleToStream; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.WildcardType; -import java.util.Iterator; -import java.util.stream.Stream; import org.apiguardian.api.API; import org.junit.jupiter.api.DynamicNode; @@ -40,7 +39,7 @@ public class IsTestFactoryMethod extends IsTestableMethod { private static final String EXPECTED_RETURN_TYPE_MESSAGE = String.format( - "must return a single %1$s or a Stream, Collection, Iterable, Iterator, or array of %1$s", + "must return a single %1$s or a Stream, Collection, Iterable, Iterator, Iterator provider, or array of %1$s", DynamicNode.class.getName()); public IsTestFactoryMethod(DiscoveryIssueReporter issueReporter) { @@ -62,9 +61,7 @@ private static boolean isCompatible(Method method, DiscoveryIssueReporter issueR issueReporter.reportIssue(createTooGenericReturnTypeIssue(method)); return true; } - boolean validContainerType = Stream.class.isAssignableFrom(returnType) // - || Iterable.class.isAssignableFrom(returnType) // - || Iterator.class.isAssignableFrom(returnType); + boolean validContainerType = !returnType.isArray() && isConvertibleToStream(returnType); return validContainerType && isCompatibleContainerType(method, issueReporter); } diff --git a/junit-jupiter-params/junit-jupiter-params.gradle.kts b/junit-jupiter-params/junit-jupiter-params.gradle.kts index 80ade03f4b27..7b3d65577455 100644 --- a/junit-jupiter-params/junit-jupiter-params.gradle.kts +++ b/junit-jupiter-params/junit-jupiter-params.gradle.kts @@ -1,3 +1,5 @@ +import junitbuild.extensions.javaModuleName + plugins { id("junitbuild.kotlin-library-conventions") id("junitbuild.shadow-conventions") diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/AnnotationBasedArgumentsProvider.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/AnnotationBasedArgumentsProvider.java index ee9a2747a6f1..3abbbe760d15 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/AnnotationBasedArgumentsProvider.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/AnnotationBasedArgumentsProvider.java @@ -10,6 +10,7 @@ package org.junit.jupiter.params.provider; +import static org.apiguardian.api.API.Status.DEPRECATED; import static org.apiguardian.api.API.Status.EXPERIMENTAL; import java.lang.annotation.Annotation; @@ -69,6 +70,7 @@ public Stream extends Arguments> provideArguments(ParameterDeclarations parame * instead. */ @Deprecated + @API(status = DEPRECATED, since = "5.13") protected Stream extends Arguments> provideArguments(ExtensionContext context, A annotation) { throw new JUnitException(String.format( "AnnotationBasedArgumentsProvider does not override the provideArguments(ParameterDeclarations, ExtensionContext, Annotation) method. " diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldSource.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldSource.java index 4ca4717fd4d7..32666855bb9c 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldSource.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldSource.java @@ -44,11 +44,13 @@ * {@link java.util.stream.DoubleStream DoubleStream}, * {@link java.util.stream.LongStream LongStream}, or * {@link java.util.stream.IntStream IntStream}), a {@code Supplier} of an - * {@link java.util.Iterator Iterator}, an array of objects, or an array of - * primitives. Each set of "arguments" within the "stream" can be supplied as an - * instance of {@link Arguments}, an array of objects (for example, {@code Object[]}, - * {@code String[]}, etc.), or a single value if the parameterized - * class or test accepts a single argument. + * {@link java.util.Iterator Iterator}, an array of objects or primitives, or + * any type that provides an {@link java.util.Iterator Iterator}-returning + * {@code iterator()} method (such as, for example, a + * {@code kotlin.sequences.Sequence}). Each set of "arguments" within the + * "stream" can be supplied as an instance of {@link Arguments}, an array of + * objects (for example, {@code Object[]}, {@code String[]}, etc.), or a single + * value if the parameterized class or test accepts a single argument. * *
In contrast to the supported return types for {@link MethodSource @MethodSource} * factory methods, the value of a {@code @FieldSource} field cannot be an instance of diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodSource.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodSource.java index aec745a5188b..d6b4d3599fa3 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodSource.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodSource.java @@ -43,12 +43,13 @@ * {@link java.util.stream.LongStream LongStream}, * {@link java.util.stream.IntStream IntStream}, * {@link java.util.Collection Collection}, - * {@link java.util.Iterator Iterator}, - * {@link Iterable}, an array of objects, or an array of primitives. Each set of - * "arguments" within the "stream" can be supplied as an instance of - * {@link Arguments}, an array of objects (e.g., {@code Object[]}, - * {@code String[]}, etc.), or a single value if the parameterized test - * method accepts a single argument. + * {@link java.util.Iterator Iterator}, an array of objects or primitives, or + * any type that provides an {@link java.util.Iterator Iterator}-returning + * {@code iterator()} method (such as, for example, a + * {@code kotlin.sequences.Sequence}). Each set of "arguments" within the + * "stream" can be supplied as an instance of {@link Arguments}, an array of + * objects (e.g., {@code Object[]}, {@code String[]}, etc.), or a single + * value if the parameterized test method accepts a single argument. * *
If the return type is {@code Stream} or * one of the primitive streams, JUnit will properly close it by calling diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/SearchOption.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/SearchOption.java index 4152d14e8220..ab3d804c5b13 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/SearchOption.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/SearchOption.java @@ -43,7 +43,8 @@ public enum SearchOption { * @deprecated because it is preferable to inspect the runtime enclosing * types of a class rather than where they are declared. */ - @Deprecated @API(status = DEPRECATED, since = "1.12") + @Deprecated // + @API(status = DEPRECATED, since = "1.12") INCLUDE_ENCLOSING_CLASSES } diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/CollectionUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/CollectionUtils.java index e12d0421f286..c50a4b178518 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/CollectionUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/CollectionUtils.java @@ -16,8 +16,10 @@ import static java.util.stream.Collectors.toList; import static java.util.stream.StreamSupport.stream; import static org.apiguardian.api.API.Status.INTERNAL; +import static org.junit.platform.commons.support.ReflectionSupport.invokeMethod; import java.lang.reflect.Array; +import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -36,6 +38,7 @@ import org.apiguardian.api.API; import org.junit.platform.commons.PreconditionViolationException; +import org.junit.platform.commons.support.ReflectionSupport; /** * Collection of utilities for working with {@link Collection Collections}. @@ -161,7 +164,8 @@ public static boolean isConvertibleToStream(Class> type) { || Iterable.class.isAssignableFrom(type)// || Iterator.class.isAssignableFrom(type)// || Object[].class.isAssignableFrom(type)// - || (type.isArray() && type.getComponentType().isPrimitive())); + || (type.isArray() && type.getComponentType().isPrimitive())// + || findIteratorMethod(type).isPresent()); } /** @@ -177,6 +181,9 @@ public static boolean isConvertibleToStream(Class> type) { *
If an engine reports an issue with a severity equal to or higher than * the configured critical severity, its tests will not be executed. - * Instead, the engine will be reported as failed during execution with a + * Depending on {@link #DISCOVERY_ISSUE_FAILURE_PHASE_PROPERTY_NAME}, a * {@link org.junit.platform.launcher.core.DiscoveryIssueException} listing - * all critical issues. + * all critical issues will be thrown during discovery or be reported as + * engine-level failure during execution. * *
Supported values are "discovery" or "execution". + * + *
If not specified, the {@code Launcher} will report discovery issues
+ * during the discovery phase if
+ * {@link Launcher#discover(LauncherDiscoveryRequest)} is called, and during
+ * the execution phase if
+ * {@link Launcher#execute(LauncherDiscoveryRequest, TestExecutionListener...)}
+ * is called.
+ *
+ * @since 1.13
+ * @see #CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME
+ */
+ @API(status = EXPERIMENTAL, since = "1.13")
+ public static final String DISCOVERY_ISSUE_FAILURE_PHASE_PROPERTY_NAME = "junit.platform.discovery.issue.failure.phase";
+
private LauncherConstants() {
/* no-op */
}
diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncher.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncher.java
index 2a9fa3bf1ed4..c4691503ba1f 100644
--- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncher.java
+++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncher.java
@@ -12,8 +12,8 @@
import static java.util.Collections.unmodifiableCollection;
import static org.junit.platform.engine.support.store.NamespacedHierarchicalStore.CloseAction.closeAutoCloseables;
-import static org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.Phase.DISCOVERY;
-import static org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.Phase.EXECUTION;
+import static org.junit.platform.launcher.core.LauncherPhase.DISCOVERY;
+import static org.junit.platform.launcher.core.LauncherPhase.EXECUTION;
import java.util.Collection;
@@ -100,8 +100,7 @@ public void execute(TestPlan testPlan, TestExecutionListener... listeners) {
execute((InternalTestPlan) testPlan, listeners);
}
- private LauncherDiscoveryResult discover(LauncherDiscoveryRequest discoveryRequest,
- EngineDiscoveryOrchestrator.Phase phase) {
+ private LauncherDiscoveryResult discover(LauncherDiscoveryRequest discoveryRequest, LauncherPhase phase) {
return discoveryOrchestrator.discover(discoveryRequest, phase);
}
diff --git a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineDiscoveryOrchestrator.java b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineDiscoveryOrchestrator.java
index 00bcdae9ada8..5c989b32d79a 100644
--- a/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineDiscoveryOrchestrator.java
+++ b/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineDiscoveryOrchestrator.java
@@ -13,12 +13,12 @@
import static java.util.stream.Collectors.joining;
import static org.apiguardian.api.API.Status.INTERNAL;
import static org.junit.platform.engine.Filter.composeFilters;
+import static org.junit.platform.launcher.core.LauncherPhase.getDiscoveryIssueFailurePhase;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
@@ -28,6 +28,7 @@
import org.junit.platform.commons.logging.Logger;
import org.junit.platform.commons.logging.LoggerFactory;
import org.junit.platform.commons.util.UnrecoverableExceptions;
+import org.junit.platform.engine.ConfigurationParameters;
import org.junit.platform.engine.Filter;
import org.junit.platform.engine.FilterResult;
import org.junit.platform.engine.TestDescriptor;
@@ -68,15 +69,19 @@ public EngineDiscoveryOrchestrator(Iterable Applies {@linkplain org.junit.platform.launcher.EngineFilter engine
* filters} and {@linkplain PostDiscoveryFilter post-discovery filters} and
* {@linkplain TestDescriptor#prune() prunes} the resulting test tree.
*/
- public LauncherDiscoveryResult discover(LauncherDiscoveryRequest request, Phase phase) {
- return discover(request, phase, UniqueId::forEngine);
+ public LauncherDiscoveryResult discover(LauncherDiscoveryRequest request) {
+ return discover(request, Optional.empty(), UniqueId::forEngine);
+ }
+
+ LauncherDiscoveryResult discover(LauncherDiscoveryRequest request, LauncherPhase phase) {
+ return discover(request, Optional.of(phase), UniqueId::forEngine);
}
/**
@@ -93,12 +98,12 @@ public LauncherDiscoveryResult discover(LauncherDiscoveryRequest request, Phase
* {@link EngineExecutionOrchestrator} will not emit start or emit events
* for engines without tests.
*/
- public LauncherDiscoveryResult discover(LauncherDiscoveryRequest request, Phase phase, UniqueId parentId) {
- LauncherDiscoveryResult result = discover(request, phase, parentId::appendEngine);
+ public LauncherDiscoveryResult discover(LauncherDiscoveryRequest request, UniqueId parentId) {
+ LauncherDiscoveryResult result = discover(request, Optional.empty(), parentId::appendEngine);
return result.withRetainedEngines(TestDescriptor::containsTests);
}
- private LauncherDiscoveryResult discover(LauncherDiscoveryRequest request, Phase phase,
+ private LauncherDiscoveryResult discover(LauncherDiscoveryRequest request, Optional
*