diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-RC1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-RC1.adoc index 7c890f6b1cce..807cde03b72f 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-RC1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-RC1.adoc @@ -26,7 +26,8 @@ repository on GitHub. [[release-notes-5.12.0-RC1-junit-platform-new-features-and-improvements]] ==== New Features and Improvements -* ❓ +* New `TestDescriptor.orderChildren(UnaryOperator> orderer)` + method to order children in place [[release-notes-5.12.0-RC1-junit-jupiter]] diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/AbstractOrderingVisitor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/AbstractOrderingVisitor.java index e2da60a53aba..0c7147d987dd 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/AbstractOrderingVisitor.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/AbstractOrderingVisitor.java @@ -10,15 +10,16 @@ package org.junit.jupiter.engine.discovery; +import static java.util.Comparator.comparing; import static java.util.stream.Collectors.toCollection; +import static java.util.stream.Collectors.toList; import java.util.ArrayList; -import java.util.LinkedHashSet; +import java.util.HashMap; import java.util.List; -import java.util.Set; +import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; -import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor; @@ -60,9 +61,8 @@ protected void doWithMatchingDescriptor(Class parentTestDescriptorType, protected void orderChildrenTestDescriptors(TestDescriptor parentTestDescriptor, Class matchingChildrenType, Function descriptorWrapperFactory, DescriptorWrapperOrderer descriptorWrapperOrderer) { - Set children = parentTestDescriptor.getChildren(); - - List matchingDescriptorWrappers = children.stream()// + List matchingDescriptorWrappers = parentTestDescriptor.getChildren()// + .stream()// .filter(matchingChildrenType::isInstance)// .map(matchingChildrenType::cast)// .map(descriptorWrapperFactory)// @@ -74,50 +74,33 @@ protected void orderChildrenTestDescriptors(TestDescriptor parentTestDescriptor, } if (descriptorWrapperOrderer.canOrderWrappers()) { - List nonMatchingTestDescriptors = children.stream()// - .filter(childTestDescriptor -> !matchingChildrenType.isInstance(childTestDescriptor))// - .collect(Collectors.toList()); - - // Make a local copy for later validation - Set originalWrappers = new LinkedHashSet<>(matchingDescriptorWrappers); - - descriptorWrapperOrderer.orderWrappers(matchingDescriptorWrappers); - - int difference = matchingDescriptorWrappers.size() - originalWrappers.size(); - if (difference > 0) { - descriptorWrapperOrderer.logDescriptorsAddedWarning(difference); - } - else if (difference < 0) { - descriptorWrapperOrderer.logDescriptorsRemovedWarning(difference); - } - - Set orderedTestDescriptors = matchingDescriptorWrappers.stream()// - .filter(originalWrappers::contains)// - .map(AbstractAnnotatedDescriptorWrapper::getTestDescriptor)// - .collect(toCollection(LinkedHashSet::new)); - - // There is currently no way to removeAll or addAll children at once, so we - // first remove them all and then add them all back. - Stream.concat(orderedTestDescriptors.stream(), nonMatchingTestDescriptors.stream())// - .forEach(parentTestDescriptor::removeChild); - - // If we are ordering children of type ClassBasedTestDescriptor, that means we - // are ordering top-level classes or @Nested test classes. Thus, the - // nonMatchingTestDescriptors list is either empty (for top-level classes) or - // contains only local test methods (for @Nested classes) which must be executed - // before tests in @Nested test classes. So we add the test methods before adding - // the @Nested test classes. - if (matchingChildrenType == ClassBasedTestDescriptor.class) { - Stream.concat(nonMatchingTestDescriptors.stream(), orderedTestDescriptors.stream())// - .forEach(parentTestDescriptor::addChild); - } - // Otherwise, we add the ordered descriptors before the non-matching descriptors, - // which is the case when we are ordering test methods. In other words, local - // test methods always get added before @Nested test classes. - else { - Stream.concat(orderedTestDescriptors.stream(), nonMatchingTestDescriptors.stream())// - .forEach(parentTestDescriptor::addChild); - } + parentTestDescriptor.orderChildren(children -> { + Stream nonMatchingTestDescriptors = children.stream()// + .filter(childTestDescriptor -> !matchingChildrenType.isInstance(childTestDescriptor)); + + descriptorWrapperOrderer.orderWrappers(matchingDescriptorWrappers); + + Stream orderedTestDescriptors = matchingDescriptorWrappers.stream()// + .map(AbstractAnnotatedDescriptorWrapper::getTestDescriptor); + + // If we are ordering children of type ClassBasedTestDescriptor, that means we + // are ordering top-level classes or @Nested test classes. Thus, the + // nonMatchingTestDescriptors list is either empty (for top-level classes) or + // contains only local test methods (for @Nested classes) which must be executed + // before tests in @Nested test classes. So we add the test methods before adding + // the @Nested test classes. + if (matchingChildrenType == ClassBasedTestDescriptor.class) { + return Stream.concat(nonMatchingTestDescriptors, orderedTestDescriptors)// + .collect(toList()); + } + // Otherwise, we add the ordered descriptors before the non-matching descriptors, + // which is the case when we are ordering test methods. In other words, local + // test methods always get added before @Nested test classes. + else { + return Stream.concat(orderedTestDescriptors, nonMatchingTestDescriptors)// + .collect(toList()); + } + }); } // Recurse through the children in order to support ordering for @Nested test classes. @@ -167,7 +150,32 @@ private boolean canOrderWrappers() { } private void orderWrappers(List wrappers) { - this.orderingAction.accept(wrappers); + List orderedWrappers = new ArrayList<>(wrappers); + this.orderingAction.accept(orderedWrappers); + Map distinctWrappersToIndex = distinctWrappersToIndex(orderedWrappers); + + int difference = orderedWrappers.size() - wrappers.size(); + int distinctDifference = distinctWrappersToIndex.size() - wrappers.size(); + if (difference > 0) { // difference >= distinctDifference + logDescriptorsAddedWarning(difference); + } + if (distinctDifference < 0) { // distinctDifference <= difference + logDescriptorsRemovedWarning(distinctDifference); + } + + wrappers.sort(comparing(wrapper -> distinctWrappersToIndex.getOrDefault(wrapper, -1))); + } + + private Map distinctWrappersToIndex(List wrappers) { + Map toIndex = new HashMap<>(); + for (int i = 0; i < wrappers.size(); i++) { + // Avoid ClassCastException if a misbehaving ordering action added a non-WRAPPER + Object wrapper = wrappers.get(i); + if (!toIndex.containsKey(wrapper)) { + toIndex.put(wrapper, i); + } + } + return toIndex; } private void logDescriptorsAddedWarning(int number) { 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 0d3df349bffa..6dfd169ab9f4 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 @@ -10,12 +10,16 @@ package org.junit.platform.engine; +import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.STABLE; +import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; +import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.function.UnaryOperator; import org.apiguardian.api.API; import org.junit.platform.commons.util.Preconditions; @@ -172,6 +176,39 @@ default Set getDescendants() { */ void removeFromHierarchy(); + /** + * Order the children from this descriptor. + * + *

The {@code orderer} is provided a modifiable list of child test + * descriptors in this test descriptor; never {@code null}. The + * {@code orderer} must return a list containing the same descriptors in any + * order; potentially the same list, but never {@code null}. If descriptors + * were added or removed, an exception is thrown. + * + * @param orderer a unary operator to order the children of this test + * descriptor. + */ + @API(since = "5.12", status = EXPERIMENTAL) + default void orderChildren(UnaryOperator> orderer) { + Preconditions.notNull(orderer, "orderer must not be null"); + Set originalChildren = getChildren(); + List suggestedOrder = orderer.apply(new ArrayList<>(originalChildren)); + Preconditions.notNull(suggestedOrder, "orderer may not return null"); + + Set orderedChildren = new LinkedHashSet<>(suggestedOrder); + boolean unmodified = originalChildren.equals(orderedChildren); + Preconditions.condition(unmodified && originalChildren.size() == suggestedOrder.size(), + "orderer may not add or remove test descriptors"); + + suggestedOrder.stream() // + .distinct() // + .filter(originalChildren::contains)// + .forEach(testDescriptor -> { + removeChild(testDescriptor); + addChild(testDescriptor); + }); + } + /** * Determine if this descriptor is a root descriptor. * diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptor.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptor.java index e8bc7920792c..336b6d686d95 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptor.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptor.java @@ -13,10 +13,13 @@ import static java.util.Collections.emptySet; import static org.apiguardian.api.API.Status.STABLE; +import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; +import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.function.UnaryOperator; import org.apiguardian.api.API; import org.junit.platform.commons.util.Preconditions; @@ -147,6 +150,24 @@ public void removeFromHierarchy() { this.children.clear(); } + /** + * {@inheritDoc} + */ + @Override + public void orderChildren(UnaryOperator> orderer) { + Preconditions.notNull(orderer, "orderer must not be null"); + List suggestedOrder = orderer.apply(new ArrayList<>(this.children)); + Preconditions.notNull(suggestedOrder, "orderer may not return null"); + + Set orderedChildren = new LinkedHashSet<>(suggestedOrder); + boolean unmodified = this.children.equals(orderedChildren); + Preconditions.condition(unmodified && this.children.size() == suggestedOrder.size(), + "orderer may not add or remove test descriptors"); + + this.children.clear(); + this.children.addAll(orderedChildren); + } + @Override public Optional findByUniqueId(UniqueId uniqueId) { Preconditions.notNull(uniqueId, "UniqueId must not be null"); diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/OrderedMethodTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/OrderedMethodTests.java index 2d859001066a..0e245db1a2a1 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/OrderedMethodTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/OrderedMethodTests.java @@ -10,6 +10,7 @@ package org.junit.jupiter.engine.extension; +import static java.util.Comparator.comparing; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.DynamicTest.dynamicTest; import static org.junit.jupiter.api.MethodOrderer.Random.RANDOM_SEED_PROPERTY_NAME; @@ -19,10 +20,14 @@ import static org.junit.jupiter.engine.Constants.PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.logging.Level; import java.util.logging.LogRecord; @@ -270,7 +275,7 @@ void misbehavingMethodOrdererThatAddsElements(@TrackLogRecords LogRecordListener executeTestsInParallel(testClass).assertStatistics(stats -> stats.succeeded(2)); - assertThat(callSequence).containsExactlyInAnyOrder("test1()", "test2()"); + assertThat(callSequence).containsExactly("test1()", "test2()"); var expectedMessage = "MethodOrderer [" + MisbehavingByAdding.class.getName() + "] added 2 MethodDescriptor(s) for test class [" + testClass.getName() + "] which will be ignored."; @@ -278,13 +283,30 @@ void misbehavingMethodOrdererThatAddsElements(@TrackLogRecords LogRecordListener assertExpectedLogMessage(listener, expectedMessage); } + @Test + void misbehavingMethodOrdererThatImpersonatesElements(@TrackLogRecords LogRecordListener listener) { + Class testClass = MisbehavingByImpersonatingTestCase.class; + + executeTestsInParallel(testClass).assertStatistics(stats -> stats.succeeded(2)); + + assertThat(callSequence).containsExactlyInAnyOrder("test1()", "test2()"); + + assertThat(listener.stream(Level.WARNING)).isEmpty(); + } + @Test void misbehavingMethodOrdererThatRemovesElements(@TrackLogRecords LogRecordListener listener) { Class testClass = MisbehavingByRemovingTestCase.class; - executeTestsInParallel(testClass).assertStatistics(stats -> stats.succeeded(3)); + executeTestsInParallel(testClass).assertStatistics(stats -> stats.succeeded(4)); - assertThat(callSequence).containsExactlyInAnyOrder("test1()", "test2()", "test3()"); + assertThat(callSequence) // + .containsExactlyInAnyOrder("test1()", "test2()", "test3()", "test4()") // + .containsSubsequence("test3()", "test4()") // ordered in MisbehavingByRemoving + .containsSubsequence("test1()", "test3()") // removed item is re-added before ordered item + .containsSubsequence("test1()", "test4()") // removed item is re-added before ordered item + .containsSubsequence("test2()", "test3()") // removed item is re-added before ordered item + .containsSubsequence("test2()", "test4()");// removed item is re-added before ordered item var expectedMessage = "MethodOrderer [" + MisbehavingByRemoving.class.getName() + "] removed 2 MethodDescriptor(s) for test class [" + testClass.getName() @@ -639,13 +661,31 @@ void trackInvocations(TestInfo testInfo) { callSequence.add(testInfo.getDisplayName()); } + @Test + void test2() { + } + @Test void test1() { } + } + + @SuppressWarnings("JUnitMalformedDeclaration") + @TestMethodOrder(MisbehavingByImpersonating.class) + static class MisbehavingByImpersonatingTestCase { + + @BeforeEach + void trackInvocations(TestInfo testInfo) { + callSequence.add(testInfo.getDisplayName()); + } @Test void test2() { } + + @Test + void test1() { + } } @SuppressWarnings("JUnitMalformedDeclaration") @@ -661,6 +701,10 @@ void trackInvocations(TestInfo testInfo) { void test1() { } + @Test + void test4() { + } + @Test void test2() { } @@ -698,6 +742,7 @@ static class MisbehavingByAdding implements MethodOrderer { @Override public void orderMethods(MethodOrdererContext context) { + context.getMethodDescriptors().sort(comparing(MethodDescriptor::getDisplayName)); context.getMethodDescriptors().add(mockMethodDescriptor()); context.getMethodDescriptors().add(mockMethodDescriptor()); } @@ -709,10 +754,66 @@ static T mockMethodDescriptor() { } + static class MisbehavingByImpersonating implements MethodOrderer { + + @Override + public void orderMethods(MethodOrdererContext context) { + context.getMethodDescriptors().sort(comparing(MethodDescriptor::getDisplayName)); + MethodDescriptor method1 = context.getMethodDescriptors().get(0); + MethodDescriptor method2 = context.getMethodDescriptors().get(1); + + context.getMethodDescriptors().set(0, createMethodDescriptorImpersonator(method1)); + context.getMethodDescriptors().set(1, createMethodDescriptorImpersonator(method2)); + } + + @SuppressWarnings("unchecked") + static T createMethodDescriptorImpersonator(MethodDescriptor method) { + MethodDescriptor stub = new MethodDescriptor() { + @Override + public Method getMethod() { + return null; + } + + @Override + public String getDisplayName() { + return null; + } + + @Override + public boolean isAnnotated(Class annotationType) { + return false; + } + + @Override + public Optional findAnnotation(Class annotationType) { + return null; + } + + @Override + public List findRepeatableAnnotations(Class annotationType) { + return null; + } + + @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") + @Override + public boolean equals(Object obj) { + return method.equals(obj); + } + + @Override + public int hashCode() { + return method.hashCode(); + } + }; + return (T) stub; + } + } + static class MisbehavingByRemoving implements MethodOrderer { @Override public void orderMethods(MethodOrdererContext context) { + context.getMethodDescriptors().sort(comparing(MethodDescriptor::getDisplayName)); context.getMethodDescriptors().remove(0); context.getMethodDescriptors().remove(0); } diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptorTests.java index 5f4fe3011fb2..bfa105bf1168 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptorTests.java @@ -32,7 +32,7 @@ * * @since 1.0 */ -class AbstractTestDescriptorTests { +class AbstractTestDescriptorTests implements TestDescriptorOrderChildrenTests { private EngineDescriptor engineDescriptor; private GroupDescriptor group1; @@ -58,6 +58,11 @@ void initTree() { group11.addChild(leaf111); } + @Override + public TestDescriptor createEmptyTestDescriptor() { + return new GroupDescriptor(UniqueId.root("group", "1")); + } + @Test void removeRootFromHierarchyFails() { var e = assertThrows(JUnitException.class, () -> engineDescriptor.removeFromHierarchy()); diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/TestDescriptorOrderChildrenTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/TestDescriptorOrderChildrenTests.java new file mode 100644 index 000000000000..55e0d80a20b9 --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/TestDescriptorOrderChildrenTests.java @@ -0,0 +1,150 @@ +/* + * Copyright 2015-2025 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.platform.engine.support.descriptor; + +import static java.util.Collections.emptyList; +import static java.util.Comparator.comparing; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.UnaryOperator; + +import org.junit.jupiter.api.Test; +import org.junit.platform.commons.PreconditionViolationException; +import org.junit.platform.engine.TestDescriptor; +import org.junit.platform.engine.UniqueId; + +public interface TestDescriptorOrderChildrenTests { + + /** + * @return a test descriptor without any children. + */ + TestDescriptor createEmptyTestDescriptor(); + + default TestDescriptor createTestDescriptorWithChildren() { + var testDescriptor = createEmptyTestDescriptor(); + testDescriptor.addChild(new StubTestDescriptor(UniqueId.root("child", "0"))); + testDescriptor.addChild(new StubTestDescriptor(UniqueId.root("child", "1"))); + testDescriptor.addChild(new StubTestDescriptor(UniqueId.root("child", "2"))); + return testDescriptor; + } + + @Test + default void orderChildrenInReverseOrder() { + var testDescriptor = createTestDescriptorWithChildren(); + var childrenInOriginalOrder = new ArrayList<>(testDescriptor.getChildren()); + testDescriptor.orderChildren(children -> { + children.sort(comparing((TestDescriptor o) -> childrenInOriginalOrder.indexOf(o)).reversed()); + return children; + }); + List children = new ArrayList<>(testDescriptor.getChildren()); + assertThat(children).isEqualTo(childrenInOriginalOrder.reversed()); + } + + @Test + default void orderChildrenEmptyList() { + var testDescriptor = createTestDescriptorWithChildren(); + var exception = assertThrows(PreconditionViolationException.class, + () -> testDescriptor.orderChildren(children -> emptyList())); + assertThat(exception).hasMessage("orderer may not add or remove test descriptors"); + } + + @Test + default void orderChildrenInSameOrder() { + var testDescriptor = createTestDescriptorWithChildren(); + var childrenInOriginalOrder = new ArrayList<>(testDescriptor.getChildren()); + testDescriptor.orderChildren(children -> { + children.sort(comparing(childrenInOriginalOrder::indexOf)); + return children; + }); + List children = new ArrayList<>(testDescriptor.getChildren()); + assertThat(children).isEqualTo(childrenInOriginalOrder); + } + + @Test + default void orderChildrenRemovesDescriptor() { + var testDescriptor = createTestDescriptorWithChildren(); + UnaryOperator> orderer = children -> { + children.remove(1); + return children; + }; + var exception = assertThrows(PreconditionViolationException.class, () -> testDescriptor.orderChildren(orderer)); + assertThat(exception).hasMessage("orderer may not add or remove test descriptors"); + } + + @Test + default void orderChildrenAddsDescriptor() { + var testDescriptor = createTestDescriptorWithChildren(); + UnaryOperator> orderer = children -> { + children.add(1, new StubTestDescriptor(UniqueId.root("extra", "extra1"))); + return children; + }; + var exception = assertThrows(PreconditionViolationException.class, () -> testDescriptor.orderChildren(orderer)); + assertThat(exception).hasMessage("orderer may not add or remove test descriptors"); + } + + @Test + default void orderChildrenReplacesDescriptor() { + var testDescriptor = createTestDescriptorWithChildren(); + UnaryOperator> orderer = children -> { + children.set(1, new StubTestDescriptor(UniqueId.root("replaced", "replaced1"))); + return children; + }; + var exception = assertThrows(PreconditionViolationException.class, () -> testDescriptor.orderChildren(orderer)); + assertThat(exception).hasMessage("orderer may not add or remove test descriptors"); + } + + @Test + default void orderChildrenDuplicatesDescriptor() { + var testDescriptor = createTestDescriptorWithChildren(); + UnaryOperator> orderer = children -> { + children.add(1, children.getLast()); + return children; + }; + var exception = assertThrows(PreconditionViolationException.class, () -> testDescriptor.orderChildren(orderer)); + assertThat(exception).hasMessage("orderer may not add or remove test descriptors"); + } + + @Test + default void orderChildrenOrdererReturnsNull() { + var testDescriptor = createTestDescriptorWithChildren(); + var exception = assertThrows(PreconditionViolationException.class, + () -> testDescriptor.orderChildren(children -> null)); + assertThat(exception).hasMessage("orderer may not return null"); + } + + @Test + default void orderChildrenProvidedChildrenAreModifiable() { + var testDescriptor = createTestDescriptorWithChildren(); + AtomicReference> childrenRef = new AtomicReference<>(); + testDescriptor.orderChildren(children -> { + childrenRef.set(children); + return children; + }); + assertThat(childrenRef.get()).isInstanceOf(ArrayList.class); + } +} + +class StubTestDescriptor extends AbstractTestDescriptor { + + StubTestDescriptor(UniqueId uniqueId) { + super(uniqueId, "stub: " + uniqueId); + } + + @Override + public Type getType() { + return Type.TEST; + } + +} diff --git a/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/TestDescriptorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/TestDescriptorTests.java new file mode 100644 index 000000000000..2d7d96308ecd --- /dev/null +++ b/platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/TestDescriptorTests.java @@ -0,0 +1,94 @@ +/* + * Copyright 2015-2025 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.platform.engine.support.descriptor; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Optional; +import java.util.Set; + +import org.junit.platform.engine.TestDescriptor; +import org.junit.platform.engine.TestSource; +import org.junit.platform.engine.TestTag; +import org.junit.platform.engine.UniqueId; + +class TestDescriptorTests implements TestDescriptorOrderChildrenTests { + + @Override + public TestDescriptor createEmptyTestDescriptor() { + return new MinimalTestDescriptorImplementation(); + } + + private static class MinimalTestDescriptorImplementation implements TestDescriptor { + + private final Set children = Collections.synchronizedSet(new LinkedHashSet<>()); + + @Override + public UniqueId getUniqueId() { + return UniqueId.root("root", "value"); + } + + @Override + public String getDisplayName() { + return "TestDescriptorImplementation"; + } + + @Override + public Set getTags() { + return Set.of(); + } + + @Override + public Optional getSource() { + return Optional.empty(); + } + + @Override + public Optional getParent() { + return Optional.empty(); + } + + @Override + public void setParent(TestDescriptor parent) { + throw new UnsupportedOperationException("Not implemented"); + } + + @Override + public Set getChildren() { + return Collections.unmodifiableSet(children); + } + + @Override + public void addChild(TestDescriptor descriptor) { + children.add(descriptor); + } + + @Override + public void removeChild(TestDescriptor descriptor) { + children.remove(descriptor); + } + + @Override + public void removeFromHierarchy() { + throw new UnsupportedOperationException("Not implemented"); + } + + @Override + public Type getType() { + return Type.CONTAINER; + } + + @Override + public Optional findByUniqueId(UniqueId uniqueId) { + throw new UnsupportedOperationException("Not implemented"); + } + } +}