diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M2.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M2.adoc index 4f40bc523b81..4def1151ca40 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M2.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M2.adoc @@ -16,7 +16,7 @@ repository on GitHub. [[release-notes-6.1.0-M2-junit-platform-bug-fixes]] ==== Bug Fixes -* ❓ +* Clarify `TestDescriptor` implementation requirements. [[release-notes-6.1.0-M2-junit-platform-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes diff --git a/documentation/src/docs/asciidoc/user-guide/advanced-topics/engines.adoc b/documentation/src/docs/asciidoc/user-guide/advanced-topics/engines.adoc index 03615613456f..8876443f0461 100644 --- a/documentation/src/docs/asciidoc/user-guide/advanced-topics/engines.adoc +++ b/documentation/src/docs/asciidoc/user-guide/advanced-topics/engines.adoc @@ -95,6 +95,8 @@ to the following requirements: * The `TestDescriptor` returned from `TestEngine.discover()` _must_ be the root of a tree of `TestDescriptor` instances. This implies that there _must not_ be any cycles between a node and its descendants. +* The hierarchy of test descriptors returned from `TestEngine.discover()` _must_ be + mutable, but the test descriptors _must_ otherwise be immutable after discovery. * A `TestEngine` _must_ be able to discover `UniqueIdSelectors` for any unique ID that it previously generated and returned from `TestEngine.discover()`. This enables selecting a subset of tests to execute or rerun. diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java index 8d146c05b616..0dc47bca49cc 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java @@ -27,8 +27,8 @@ import org.junit.platform.commons.util.Preconditions; /** - * Mutable descriptor for a test or container that has been discovered by a - * {@link TestEngine}. + * A descriptor with a mutable hierarchy for a test or container that has been + * discovered by a {@link TestEngine}. * * @since 1.0 * @see TestEngine @@ -42,6 +42,9 @@ public interface TestDescriptor { *

Uniqueness must be guaranteed across an entire test plan, * regardless of how many engines are used behind the scenes. * + *

The implementation must treat this property as immutable after test + * discovery has completed. + * * @return the {@code UniqueId} for this descriptor; never {@code null} */ UniqueId getUniqueId(); @@ -77,6 +80,9 @@ default String getLegacyReportingName() { /** * Get the set of {@linkplain TestTag tags} associated with this descriptor. * + *

The implementation must treat this property as immutable after test + * discovery has completed. + * * @return the set of tags associated with this descriptor; never {@code null} * but potentially empty * @see TestTag @@ -87,6 +93,9 @@ default String getLegacyReportingName() { * Get the {@linkplain TestSource source} of the test or container described * by this descriptor, if available. * + *

The implementation must treat this property as immutable after test + * discovery has completed. + * * @see TestSource */ Optional getSource(); @@ -106,6 +115,9 @@ default String getLegacyReportingName() { /** * Get the immutable set of children of this descriptor. * + *

The implementation must be consistent with {@link #isContainer()} such that + * {@code !x.container()} implies {@code x.getChildren().isEmpty()}. + * * @return the set of children of this descriptor; neither {@code null} * nor mutable, but potentially empty * @see #getDescendants() @@ -141,6 +153,9 @@ default Set getAncestors() { *

A descendant is a child of this descriptor or a child of one of * its children, recursively. * + *

The implementation must be consistent with {@link #isContainer()} such that + * {@code !x.container()} implies {@code x.getDescendants().isEmpty()}. + * * @see #getChildren() */ default Set getDescendants() { @@ -223,6 +238,9 @@ default boolean isRoot() { /** * Determine the {@link Type} of this descriptor. * + *

The implementation must treat this property as immutable after test + * discovery has completed. + * * @return the descriptor type; never {@code null}. * @see #isContainer() * @see #isTest() @@ -230,7 +248,14 @@ default boolean isRoot() { Type getType(); /** - * Determine if this descriptor describes a container. + * Determine if this descriptor describes a container. + * + *

A test descriptor is a container when it may contain other + * containers or tests as its children. In addition to being a + * container this test descriptor may also be a test. + * + *

The implementation must be consistent with {@link #getType()} such + * that {@code x.isContainer()} equals {@code x.getType().isContainer()}. * *

The default implementation delegates to {@link Type#isContainer()}. */ @@ -239,7 +264,14 @@ default boolean isContainer() { } /** - * Determine if this descriptor describes a test. + * Determine if this descriptor describes a test. + * + *

A test descriptor is a test when it verifies expected + * behavior when executed. In addition to being a test this + * test descriptor may also be a container. + * + *

The implementation must be consistent with {@link #getType()} such + * that {@code x.isTest()} equals {@code x.getType().isTest()}. * *

The default implementation delegates to {@link Type#isTest()}. */ @@ -250,6 +282,10 @@ default boolean isTest() { /** * Determine if this descriptor may register dynamic tests during execution. * + *

The implementation must treat this property as immutable after test + * discovery has completed and must be consistent with {@link #isContainer()} + * such that {@code !x.container()} implies {@code !x.mayRegisterTests()}. + * *

The default implementation assumes tests are usually known during * discovery and thus returns {@code false}. */ @@ -259,7 +295,7 @@ default boolean mayRegisterTests() { /** * Determine if the supplied descriptor (or any of its descendants) - * {@linkplain TestDescriptor#isTest() is a test} or + * {@linkplain TestDescriptor#isTest() is a test} or * {@linkplain TestDescriptor#mayRegisterTests() may potentially register * tests dynamically}. * @@ -355,12 +391,14 @@ static Visitor composite(Visitor... visitors) { enum Type { /** - * Denotes that the {@link TestDescriptor} is for a container. + * Denotes that the {@link TestDescriptor} is strictly for a + * container. I.e. it is not also a test. */ CONTAINER, /** - * Denotes that the {@link TestDescriptor} is for a test. + * Denotes that the {@link TestDescriptor} is strictly for a + * test. I.e. it is not also a container. */ TEST,