From 6f15fa5d805ed2f254387b7f650ab75f0fd022e9 Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Mon, 30 Jun 2025 17:22:10 +0200 Subject: [PATCH 1/2] Use JUnit's EngineTestKit for testing --- microbenchmark-runner-junit5/pom.xml | 6 ++ ...va => DiscoverySelectorResolverTests.java} | 73 ++++++++++--------- 2 files changed, 43 insertions(+), 36 deletions(-) rename microbenchmark-runner-junit5/src/test/java/jmh/mbr/junit5/discovery/{JavaElementsResolverUnitTests.java => DiscoverySelectorResolverTests.java} (56%) diff --git a/microbenchmark-runner-junit5/pom.xml b/microbenchmark-runner-junit5/pom.xml index ba68d5a..89c8b88 100644 --- a/microbenchmark-runner-junit5/pom.xml +++ b/microbenchmark-runner-junit5/pom.xml @@ -51,6 +51,12 @@ test + + org.junit.platform + junit-platform-testkit + test + + diff --git a/microbenchmark-runner-junit5/src/test/java/jmh/mbr/junit5/discovery/JavaElementsResolverUnitTests.java b/microbenchmark-runner-junit5/src/test/java/jmh/mbr/junit5/discovery/DiscoverySelectorResolverTests.java similarity index 56% rename from microbenchmark-runner-junit5/src/test/java/jmh/mbr/junit5/discovery/JavaElementsResolverUnitTests.java rename to microbenchmark-runner-junit5/src/test/java/jmh/mbr/junit5/discovery/DiscoverySelectorResolverTests.java index 3c953a9..a6d0c0e 100644 --- a/microbenchmark-runner-junit5/src/test/java/jmh/mbr/junit5/discovery/JavaElementsResolverUnitTests.java +++ b/microbenchmark-runner-junit5/src/test/java/jmh/mbr/junit5/discovery/DiscoverySelectorResolverTests.java @@ -9,45 +9,42 @@ */ package jmh.mbr.junit5.discovery; +import static org.assertj.core.api.Assertions.*; +import static org.junit.platform.engine.discovery.ClassNameFilter.*; +import static org.junit.platform.engine.discovery.DiscoverySelectors.*; + +import jmh.mbr.junit5.MicrobenchmarkEngine; import jmh.mbr.junit5.PartiallyParametrizedBenchmark; import jmh.mbr.junit5.descriptor.BenchmarkClassDescriptor; import jmh.mbr.junit5.descriptor.BenchmarkMethodDescriptor; import jmh.mbr.junit5.descriptor.ParametrizedBenchmarkMethodDescriptor; -import static org.assertj.core.api.Assertions.*; +import java.util.regex.Pattern; + import org.junit.jupiter.api.Test; -import org.junit.platform.commons.support.scanning.ClassFilter; +import org.junit.platform.engine.Filter; import org.junit.platform.engine.TestDescriptor; -import org.junit.platform.engine.UniqueId; -import org.junit.platform.engine.discovery.DiscoverySelectors; -import org.junit.platform.engine.support.descriptor.AbstractTestDescriptor; +import org.junit.platform.testkit.engine.EngineDiscoveryResults; +import org.junit.platform.testkit.engine.EngineTestKit; /** - * Unit tests for {@link JavaElementsResolver}. + * Tests for {@link DiscoverySelectorResolver}. */ -class JavaElementsResolverUnitTests { - - private final TestDescriptor ENGINE = new AbstractTestDescriptor(UniqueId.forEngine("foo"), "foo") { - @Override - public Type getType() { - return Type.CONTAINER; - } - }; - - private final ClassFilter FILTER = ClassFilter.of(it -> true); +class DiscoverySelectorResolverTests { @Test void shouldResolveClassByPackageSelector() { - JavaElementsResolver resolver = new JavaElementsResolver(ENGINE, - ClassFilter.of(it -> it.equals(PartiallyParametrizedBenchmark.class)), ElementResolvers.getResolvers()); + EngineDiscoveryResults results = EngineTestKit.engine(new MicrobenchmarkEngine()) + .selectors(selectPackage(PartiallyParametrizedBenchmark.class.getPackage().getName())) + .filters((Filter) includeClassNamePatterns(Pattern.quote(PartiallyParametrizedBenchmark.class.getName()))) + .discover(); - resolver - .resolvePackage(DiscoverySelectors.selectPackage(PartiallyParametrizedBenchmark.class.getPackage().getName())); + TestDescriptor engineDescriptor = results.getEngineDescriptor(); - assertThat(ENGINE.getChildren()).hasSize(1); + assertThat(engineDescriptor.getChildren()).hasSize(1); - TestDescriptor classDescriptor = ENGINE.getChildren().iterator().next(); + TestDescriptor classDescriptor = engineDescriptor.getChildren().iterator().next(); assertBenchmarkClass(classDescriptor); @@ -60,13 +57,14 @@ void shouldResolveClassByPackageSelector() { @Test void shouldResolveClassByClassSelector() { - JavaElementsResolver resolver = new JavaElementsResolver(ENGINE, FILTER, ElementResolvers.getResolvers()); + EngineDiscoveryResults results = EngineTestKit.engine(new MicrobenchmarkEngine()) + .selectors(selectClass(PartiallyParametrizedBenchmark.class)).discover(); - resolver.resolveClass(DiscoverySelectors.selectClass(PartiallyParametrizedBenchmark.class)); + TestDescriptor engineDescriptor = results.getEngineDescriptor(); - assertThat(ENGINE.getChildren()).hasSize(1); + assertThat(engineDescriptor.getChildren()).hasSize(1); - TestDescriptor classDescriptor = ENGINE.getChildren().iterator().next(); + TestDescriptor classDescriptor = engineDescriptor.getChildren().iterator().next(); assertBenchmarkClass(classDescriptor); @@ -79,14 +77,16 @@ void shouldResolveClassByClassSelector() { @Test void shouldResolveBenchmarkMethodByMethodSelector() { - JavaElementsResolver resolver = new JavaElementsResolver(ENGINE, FILTER, ElementResolvers.getResolvers()); + EngineDiscoveryResults results = EngineTestKit.engine(new MicrobenchmarkEngine()) + .selectors(selectMethod(PartiallyParametrizedBenchmark.class, "bar", + "jmh.mbr.junit5.PartiallyParametrizedBenchmark$ParamState")) + .discover(); - resolver.resolveMethod(DiscoverySelectors.selectMethod(PartiallyParametrizedBenchmark.class, "bar", - "jmh.mbr.junit5.PartiallyParametrizedBenchmark$ParamState")); + TestDescriptor engineDescriptor = results.getEngineDescriptor(); - assertThat(ENGINE.getChildren()).hasSize(1); + assertThat(engineDescriptor.getChildren()).hasSize(1); - TestDescriptor classDescriptor = ENGINE.getChildren().iterator().next(); + TestDescriptor classDescriptor = engineDescriptor.getChildren().iterator().next(); assertBenchmarkClass(classDescriptor); @@ -99,14 +99,15 @@ void shouldResolveBenchmarkMethodByMethodSelector() { @Test void shouldResolveBenchmarkMethodByUniqueIdSelector() { - JavaElementsResolver resolver = new JavaElementsResolver(ENGINE, FILTER, ElementResolvers.getResolvers()); + EngineDiscoveryResults results = EngineTestKit.engine(new MicrobenchmarkEngine()).selectors(selectUniqueId( + "[engine:microbenchmark-engine]/[class:jmh.mbr.junit5.PartiallyParametrizedBenchmark]/[method:bar(jmh.mbr.junit5.PartiallyParametrizedBenchmark$ParamState)]/[fixture:%5Bfoo=b%5D]")) + .discover(); - resolver.resolveUniqueId(DiscoverySelectors.selectUniqueId( - "[engine:microbenchmark-engine]/[class:jmh.mbr.junit5.PartiallyParametrizedBenchmark]/[method:bar(jmh.mbr.junit5.PartiallyParametrizedBenchmark$ParamState)]/[fixture:%5Bfoo=b%5D]")); + TestDescriptor engineDescriptor = results.getEngineDescriptor(); - assertThat(ENGINE.getChildren()).hasSize(1); + assertThat(engineDescriptor.getChildren()).hasSize(1); - TestDescriptor classDescriptor = ENGINE.getChildren().iterator().next(); + TestDescriptor classDescriptor = engineDescriptor.getChildren().iterator().next(); assertBenchmarkClass(classDescriptor); From 844938a5691609339b059201ff32daf8173e13fc Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Mon, 30 Jun 2025 17:28:57 +0200 Subject: [PATCH 2/2] Refactor discovery to use JUnit's `EngineDiscoveryRequestResolver` This commit removes calls to deprecated JUnit APIs and adopts the `EngineDiscoveryRequestResolver` approach to test discovery. --- .../discovery/BenchmarkContainerResolver.java | 101 +++---- .../discovery/BenchmarkFixtureResolver.java | 95 +++---- .../discovery/BenchmarkMethodResolver.java | 122 ++++---- .../discovery/DiscoveryFilterApplier.java | 36 --- .../discovery/DiscoverySelectorResolver.java | 54 +--- .../mbr/junit5/discovery/ElementResolver.java | 35 --- .../junit5/discovery/ElementResolvers.java | 32 --- .../discovery/JavaElementsResolver.java | 266 ------------------ 8 files changed, 154 insertions(+), 587 deletions(-) delete mode 100644 microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/DiscoveryFilterApplier.java delete mode 100644 microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/ElementResolver.java delete mode 100644 microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/ElementResolvers.java delete mode 100644 microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/JavaElementsResolver.java diff --git a/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/BenchmarkContainerResolver.java b/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/BenchmarkContainerResolver.java index 337bc34..1043aeb 100644 --- a/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/BenchmarkContainerResolver.java +++ b/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/BenchmarkContainerResolver.java @@ -9,95 +9,80 @@ */ package jmh.mbr.junit5.discovery; +import static java.util.stream.Collectors.*; +import static org.junit.platform.engine.discovery.DiscoverySelectors.*; +import static org.junit.platform.engine.support.discovery.SelectorResolver.Resolution.*; + import jmh.mbr.core.model.BenchmarkClass; import jmh.mbr.core.model.BenchmarkDescriptorFactory; import jmh.mbr.junit5.descriptor.BenchmarkClassDescriptor; import jmh.mbr.junit5.discovery.predicates.IsBenchmarkClass; +import jmh.mbr.junit5.discovery.predicates.IsBenchmarkMethod; -import java.lang.reflect.AnnotatedElement; -import java.util.Collections; import java.util.Optional; -import java.util.Set; +import java.util.function.Predicate; -import org.junit.platform.commons.util.ReflectionUtils; +import org.junit.platform.commons.support.HierarchyTraversalMode; +import org.junit.platform.commons.support.ReflectionSupport; import org.junit.platform.engine.TestDescriptor; import org.junit.platform.engine.UniqueId; +import org.junit.platform.engine.discovery.ClassSelector; +import org.junit.platform.engine.discovery.UniqueIdSelector; +import org.junit.platform.engine.support.discovery.SelectorResolver; /** - * {@link ElementResolver} for test containers. Containers are based on {@link Class classes} that contain + * {@link SelectorResolver} for test containers. Containers are based on {@link Class classes} that contain * {@code Benchmark} methods. * * @see IsBenchmarkClass */ -class BenchmarkContainerResolver implements ElementResolver { +class BenchmarkContainerResolver implements SelectorResolver { private static final String SEGMENT_TYPE = "class"; - BenchmarkContainerResolver() {} - - @Override - public Set resolveElement(AnnotatedElement element, TestDescriptor parent) { - - if (!(element instanceof Class)) { - return Collections.emptySet(); - } + private final Predicate classNameFilter; - Class clazz = (Class) element; - if (!isPotentialCandidate(clazz)) { - return Collections.emptySet(); - } - - UniqueId uniqueId = createUniqueId(clazz, parent); - return Collections.singleton(resolveClass(clazz, uniqueId)); + BenchmarkContainerResolver(Predicate classNameFilter) { + this.classNameFilter = classNameFilter; } @Override - public Optional resolveUniqueId(UniqueId.Segment segment, TestDescriptor parent) { - - if (!segment.getType().equals(SEGMENT_TYPE)) { - return Optional.empty(); + public Resolution resolve(ClassSelector selector, Context context) { + if (classNameFilter.test(selector.getClassName())) { + return resolveClass(selector.getJavaClass(), context); } + return Resolution.unresolved(); + } - if (!requiredParentType().isInstance(parent)) { - return Optional.empty(); - } - - String className = getClassName(parent, segment.getValue()); - - Optional> optionalContainerClass = ReflectionUtils.loadClass(className); - if (!optionalContainerClass.isPresent()) { - return Optional.empty(); - } + @Override + public Resolution resolve(UniqueIdSelector selector, Context context) { + UniqueId uniqueId = selector.getUniqueId(); + UniqueId.Segment lastSegment = uniqueId.getLastSegment(); - Class containerClass = optionalContainerClass.get(); - if (!isPotentialCandidate(containerClass)) { - return Optional.empty(); + if (lastSegment.getType().equals(SEGMENT_TYPE)) { + return ReflectionSupport.tryToLoadClass(lastSegment.getValue()).toOptional() + .map(testClass -> resolveClass(testClass, context)).orElse(unresolved()); } - UniqueId uniqueId = createUniqueId(containerClass, parent); - return Optional.of(resolveClass(containerClass, uniqueId)); + return unresolved(); } - private Class requiredParentType() { - return TestDescriptor.class; - } - - private String getClassName(TestDescriptor parent, String segmentValue) { - return segmentValue; - } - - private boolean isPotentialCandidate(Class element) { - return IsBenchmarkClass.INSTANCE.test(element); + private Resolution resolveClass(Class testClass, Context context) { + if (IsBenchmarkClass.INSTANCE.test(testClass)) { + return context.addToParent(parent -> createClassDescriptor(testClass, parent)) + .map(descriptor -> Resolution.match(Match.exact(descriptor, + () -> ReflectionSupport + .streamMethods(testClass, IsBenchmarkMethod.INSTANCE, HierarchyTraversalMode.TOP_DOWN) + .map(m -> selectMethod(testClass, m)).collect(toSet())))) // + .orElse(unresolved()); + } + return unresolved(); } - private UniqueId createUniqueId(Class benchmarkClass, TestDescriptor parent) { - return parent.getUniqueId().append(SEGMENT_TYPE, benchmarkClass.getName()); + private static Optional createClassDescriptor(Class testClass, TestDescriptor parent) { + UniqueId uniqueId = parent.getUniqueId().append(BenchmarkContainerResolver.SEGMENT_TYPE, testClass.getName()); + BenchmarkClass descriptor = BenchmarkDescriptorFactory.create(testClass).createDescriptor(); + return Optional.of(new BenchmarkClassDescriptor(uniqueId, descriptor)); } - private TestDescriptor resolveClass(Class benchmarkClass, UniqueId uniqueId) { - - BenchmarkClass descriptor = BenchmarkDescriptorFactory.create(benchmarkClass).createDescriptor(); - - return new BenchmarkClassDescriptor(uniqueId, descriptor); - } } diff --git a/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/BenchmarkFixtureResolver.java b/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/BenchmarkFixtureResolver.java index e2f9d4d..f3537e9 100644 --- a/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/BenchmarkFixtureResolver.java +++ b/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/BenchmarkFixtureResolver.java @@ -9,86 +9,55 @@ */ package jmh.mbr.junit5.discovery; +import static org.junit.platform.engine.discovery.DiscoverySelectors.*; +import static org.junit.platform.engine.support.discovery.SelectorResolver.Resolution.*; + +import jmh.mbr.core.model.BenchmarkFixture; +import jmh.mbr.core.model.BenchmarkMethod; import jmh.mbr.core.model.ParametrizedBenchmarkMethod; +import jmh.mbr.junit5.descriptor.AbstractBenchmarkDescriptor; import jmh.mbr.junit5.descriptor.BenchmarkFixtureDescriptor; import jmh.mbr.junit5.descriptor.ParametrizedBenchmarkMethodDescriptor; -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Method; -import java.util.Collections; import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; import org.junit.platform.engine.TestDescriptor; import org.junit.platform.engine.UniqueId; -import org.junit.platform.engine.UniqueId.Segment; +import org.junit.platform.engine.discovery.UniqueIdSelector; +import org.junit.platform.engine.support.discovery.SelectorResolver; /** - * {@link ElementResolver} for {@link jmh.mbr.core.model.BenchmarkFixture fixtures}. + * {@link SelectorResolver} for {@link jmh.mbr.core.model.BenchmarkFixture fixtures}. */ -class BenchmarkFixtureResolver implements ElementResolver { - - private static final String SEGMENT_TYPE = "fixture"; - - @Override - public Set resolveElement(AnnotatedElement element, TestDescriptor parent) { - - if (!(element instanceof Method)) { - return Collections.emptySet(); - } - - if (!(parent instanceof ParametrizedBenchmarkMethodDescriptor)) { - return Collections.emptySet(); - } - - ParametrizedBenchmarkMethodDescriptor parentDescriptor = (ParametrizedBenchmarkMethodDescriptor) parent; - - Method method = (Method) element; - - if (!parentDescriptor.isUnderlyingMethod(method)) { - return Collections.emptySet(); - } +class BenchmarkFixtureResolver implements SelectorResolver { - return findFixtures(parent.getUniqueId(), parentDescriptor.getParametrizedMethod()); - } + static final String SEGMENT_TYPE = "fixture"; @Override - public Optional resolveUniqueId(Segment segment, TestDescriptor parent) { - - if (!segment.getType().equals(SEGMENT_TYPE)) { - return Optional.empty(); - } - - if (!(parent instanceof ParametrizedBenchmarkMethodDescriptor)) { - return Optional.empty(); + public Resolution resolve(UniqueIdSelector selector, Context context) { + + UniqueId uniqueId = selector.getUniqueId(); + UniqueId.Segment lastSegment = uniqueId.getLastSegment(); + + if (lastSegment.getType().equals(BenchmarkFixtureResolver.SEGMENT_TYPE)) { + return context + . addToParent(() -> selectUniqueId(uniqueId.removeLastSegment()), parent -> { + ParametrizedBenchmarkMethodDescriptor methodDescriptor = (ParametrizedBenchmarkMethodDescriptor) parent; + ParametrizedBenchmarkMethod parametrizedMethod = methodDescriptor.getParametrizedMethod(); + return parametrizedMethod.getChildren().stream() // + .filter(fixture -> fixture.getDisplayName().equals(lastSegment.getValue())) // + .findFirst() // + .flatMap(fixture -> Optional + .of(createFixtureDescriptor(parent, parametrizedMethod.getDescriptor(), fixture))); + }).map(descriptor -> Resolution.match(Match.exact(descriptor))).orElse(unresolved()); } - ParametrizedBenchmarkMethodDescriptor parentDescriptor = (ParametrizedBenchmarkMethodDescriptor) parent; - - return findTestDescriptor(segment.getValue(), parentDescriptor.getUniqueId(), - parentDescriptor.getParametrizedMethod()); - } - - private Set findFixtures(UniqueId parentId, ParametrizedBenchmarkMethod descriptor) { - - return descriptor.getChildren().stream().map(it -> { - - UniqueId uniqueId = parentId.append(SEGMENT_TYPE, it.getDisplayName()); - return new BenchmarkFixtureDescriptor(uniqueId, descriptor.getDescriptor(), it); - }).collect(Collectors.toSet()); + return unresolved(); } - private Optional findTestDescriptor(String segmentValue, UniqueId parentId, - ParametrizedBenchmarkMethod parametrizedMethod) { - - return parametrizedMethod.getChildren().stream().filter(it -> { - return it.getDisplayName().equals(segmentValue); - }).map(it -> { - - UniqueId uniqueId = parentId.append(SEGMENT_TYPE, it.getDisplayName()); - - return (TestDescriptor) new BenchmarkFixtureDescriptor(uniqueId, parametrizedMethod.getDescriptor(), it); - }).findFirst(); + private BenchmarkFixtureDescriptor createFixtureDescriptor(TestDescriptor parent, BenchmarkMethod method, + BenchmarkFixture fixture) { + UniqueId uniqueId = parent.getUniqueId().append(BenchmarkFixtureResolver.SEGMENT_TYPE, fixture.getDisplayName()); + return new BenchmarkFixtureDescriptor(uniqueId, method, fixture); } } diff --git a/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/BenchmarkMethodResolver.java b/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/BenchmarkMethodResolver.java index 2ffa74c..49d16f7 100644 --- a/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/BenchmarkMethodResolver.java +++ b/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/BenchmarkMethodResolver.java @@ -9,103 +9,115 @@ */ package jmh.mbr.junit5.discovery; -import jmh.mbr.core.model.BenchmarkClass; +import static java.util.stream.Collectors.*; +import static org.junit.platform.engine.discovery.DiscoverySelectors.*; +import static org.junit.platform.engine.support.discovery.SelectorResolver.Resolution.*; + import jmh.mbr.core.model.BenchmarkDescriptor; import jmh.mbr.core.model.BenchmarkMethod; import jmh.mbr.core.model.MethodAware; import jmh.mbr.core.model.ParametrizedBenchmarkMethod; +import jmh.mbr.junit5.descriptor.AbstractBenchmarkDescriptor; import jmh.mbr.junit5.descriptor.BenchmarkClassDescriptor; import jmh.mbr.junit5.descriptor.BenchmarkMethodDescriptor; import jmh.mbr.junit5.descriptor.ParametrizedBenchmarkMethodDescriptor; +import jmh.mbr.junit5.discovery.predicates.IsBenchmarkMethod; -import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; -import java.util.Collections; import java.util.Optional; -import java.util.Set; import org.junit.platform.engine.TestDescriptor; import org.junit.platform.engine.UniqueId; -import org.junit.platform.engine.UniqueId.Segment; +import org.junit.platform.engine.discovery.MethodSelector; +import org.junit.platform.engine.discovery.UniqueIdSelector; +import org.junit.platform.engine.support.discovery.SelectorResolver; /** - * {@link ElementResolver} for {@code Benchmark} {@link Method methods}. + * {@link SelectorResolver} for {@code Benchmark} {@link Method methods}. */ -class BenchmarkMethodResolver implements ElementResolver { +class BenchmarkMethodResolver implements SelectorResolver { private static final String SEGMENT_TYPE = "method"; @Override - public Set resolveElement(AnnotatedElement element, TestDescriptor parent) { - - if (!(element instanceof Method)) { - return Collections.emptySet(); - } - - if (!(parent instanceof BenchmarkClassDescriptor)) { - return Collections.emptySet(); - } - - Method method = (Method) element; - BenchmarkClassDescriptor classDescriptor = (BenchmarkClassDescriptor) parent; - - if (!method.getDeclaringClass().isAssignableFrom(classDescriptor.getJavaClass())) { - return Collections.emptySet(); + public Resolution resolve(MethodSelector selector, Context context) { + Method method = selector.getJavaMethod(); + if (IsBenchmarkMethod.INSTANCE.test(method)) { + return context + .addToParent(() -> selectClass(method.getDeclaringClass()), + parent -> createMethodDescriptor((BenchmarkClassDescriptor) parent, method)) + .map(BenchmarkMethodResolver::toResolution).orElse(unresolved()); } - - return findMethod(parent.getUniqueId(), classDescriptor.getBenchmarkClass(), method).map(Collections::singleton) - .orElseGet(Collections::emptySet); + return unresolved(); } @Override - public Optional resolveUniqueId(Segment segment, TestDescriptor parent) { - - if (!segment.getType().equals(SEGMENT_TYPE)) { - return Optional.empty(); + public Resolution resolve(UniqueIdSelector selector, Context context) { + + UniqueId uniqueId = selector.getUniqueId(); + UniqueId.Segment lastSegment = uniqueId.getLastSegment(); + + if (lastSegment.getType().equals(SEGMENT_TYPE)) { + return context // + .addToParent(() -> selectUniqueId(uniqueId.removeLastSegment()), parent -> { + BenchmarkClassDescriptor classDescriptor = (BenchmarkClassDescriptor) parent; + return findBenchmarkDescriptor(classDescriptor, lastSegment) // + .flatMap(it -> createMethodDescriptor(classDescriptor, ((MethodAware) it).getMethod())); + }) // + .map(BenchmarkMethodResolver::toResolution) // + .orElse(unresolved()); } - if (!(parent instanceof BenchmarkClassDescriptor)) { - return Optional.empty(); - } - - BenchmarkClassDescriptor descriptor = (BenchmarkClassDescriptor) parent; - - return findMethod(segment, parent.getUniqueId(), descriptor.getBenchmarkClass()); + return unresolved(); } - private Optional findMethod(UniqueId parentId, BenchmarkClass benchmarkClass, Method method) { - - return benchmarkClass.getChildren().stream() // - .filter(MethodAware.class::isInstance) // - .filter(it -> ((MethodAware) it).isUnderlyingMethod(method)) // - .map(it -> createDescriptor(parentId, it)).findFirst(); - } - - private Optional findMethod(Segment segment, UniqueId parentId, BenchmarkClass benchmarkClass) { - - return benchmarkClass.getChildren().stream() // + private static Optional findBenchmarkDescriptor( + BenchmarkClassDescriptor classDescriptor, UniqueId.Segment lastSegment) { + return classDescriptor.getBenchmarkClass().getChildren().stream() // .filter(MethodAware.class::isInstance) // - .filter(it -> { + .filter(benchmarkDescriptor -> { - Method method = ((MethodAware) it).getMethod(); + Method method = ((MethodAware) benchmarkDescriptor).getMethod(); String id = BenchmarkMethodDescriptor.describeMethodId(method); - return segment.getValue().equals(id); + return lastSegment.getValue().equals(id); }) // - .map(it -> createDescriptor(parentId, it)) // .findFirst(); } - private TestDescriptor createDescriptor(UniqueId parentId, BenchmarkDescriptor it) { + private static Resolution toResolution(TestDescriptor descriptor) { + if (descriptor instanceof ParametrizedBenchmarkMethodDescriptor) { + ParametrizedBenchmarkMethodDescriptor parametrizedMethodDescriptor = (ParametrizedBenchmarkMethodDescriptor) descriptor; + return Resolution.match(Match.exact(descriptor, + () -> parametrizedMethodDescriptor.getParametrizedMethod().getChildren().stream() // + .map(it -> selectUniqueId( + descriptor.getUniqueId().append(BenchmarkFixtureResolver.SEGMENT_TYPE, it.getDisplayName()))) // + .collect(toSet()))); + } + return Resolution.match(Match.exact(descriptor)); + } + + private static Optional createMethodDescriptor(BenchmarkClassDescriptor parent, Method method) { + return parent.getBenchmarkClass().getChildren().stream() // + .filter(MethodAware.class::isInstance) + .filter(benchmarkDescriptor -> ((MethodAware) benchmarkDescriptor).isUnderlyingMethod(method)) // + .findFirst() // + .map(benchmarkDescriptor -> toMethodDescriptor(parent, benchmarkDescriptor)); + } + + private static AbstractBenchmarkDescriptor toMethodDescriptor(BenchmarkClassDescriptor parent, + BenchmarkDescriptor benchmarkDescriptor) { + + UniqueId parentId = parent.getUniqueId(); - if (it instanceof ParametrizedBenchmarkMethod) { - ParametrizedBenchmarkMethod parametrized = (ParametrizedBenchmarkMethod) it; + if (benchmarkDescriptor instanceof ParametrizedBenchmarkMethod) { + ParametrizedBenchmarkMethod parametrized = (ParametrizedBenchmarkMethod) benchmarkDescriptor; UniqueId uniqueId = BenchmarkMethodDescriptor.createUniqueId(parentId, parametrized.getDescriptor()); return new ParametrizedBenchmarkMethodDescriptor(uniqueId, parametrized); } - BenchmarkMethod benchmarkMethod = (BenchmarkMethod) it; + BenchmarkMethod benchmarkMethod = (BenchmarkMethod) benchmarkDescriptor; UniqueId uniqueId = BenchmarkMethodDescriptor.createUniqueId(parentId, benchmarkMethod); return new BenchmarkMethodDescriptor(uniqueId, benchmarkMethod); diff --git a/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/DiscoveryFilterApplier.java b/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/DiscoveryFilterApplier.java deleted file mode 100644 index 5c0060d..0000000 --- a/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/DiscoveryFilterApplier.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2018 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 - * - * http://www.eclipse.org/legal/epl-v20.html - */ -package jmh.mbr.junit5.discovery; - -import jmh.mbr.junit5.descriptor.BenchmarkClassDescriptor; - -import java.util.function.Predicate; - -import org.junit.platform.engine.TestDescriptor; - -/** - * Class for applying filters to all children of a {@link TestDescriptor}. - */ -class DiscoveryFilterApplier { - - void applyClassNamePredicate(Predicate classNamePredicate, TestDescriptor engineDescriptor) { - TestDescriptor.Visitor filteringVisitor = descriptor -> { - if (descriptor instanceof BenchmarkClassDescriptor - && !includeClass((BenchmarkClassDescriptor) descriptor, classNamePredicate)) { - descriptor.removeFromHierarchy(); - } - }; - engineDescriptor.accept(filteringVisitor); - } - - private boolean includeClass(BenchmarkClassDescriptor classTestDescriptor, Predicate classNamePredicate) { - return classNamePredicate.test(classTestDescriptor.getJavaClass().getName()); - } -} diff --git a/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/DiscoverySelectorResolver.java b/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/DiscoverySelectorResolver.java index a728252..6b5290f 100644 --- a/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/DiscoverySelectorResolver.java +++ b/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/DiscoverySelectorResolver.java @@ -11,56 +11,26 @@ import jmh.mbr.junit5.discovery.predicates.IsBenchmarkClass; -import org.junit.platform.commons.support.scanning.ClassFilter; -import org.junit.platform.engine.ConfigurationParameters; import org.junit.platform.engine.EngineDiscoveryRequest; import org.junit.platform.engine.TestDescriptor; -import org.junit.platform.engine.discovery.ClassSelector; -import org.junit.platform.engine.discovery.ClasspathRootSelector; -import org.junit.platform.engine.discovery.MethodSelector; -import org.junit.platform.engine.discovery.ModuleSelector; -import org.junit.platform.engine.discovery.PackageSelector; -import org.junit.platform.engine.discovery.UniqueIdSelector; -import org.junit.platform.engine.support.filter.ClasspathScanningSupport; +import org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver; /** - * {@link JavaElementsResolver}-based discovery mechanism. Resolves {@link TestDescriptor descriptors} by introspecting - * classes, methods, and {@link org.junit.platform.engine.UniqueId}. + * {@link EngineDiscoveryRequestResolver}-based discovery mechanism. Resolves {@link TestDescriptor descriptors} by + * introspecting classes, methods, and {@link org.junit.platform.engine.UniqueId}. */ public class DiscoverySelectorResolver { - public void resolveSelectors(EngineDiscoveryRequest request, TestDescriptor engineDescriptor) { - - ClassFilter classFilter = ClasspathScanningSupport.buildClassFilter(request, IsBenchmarkClass.INSTANCE); - resolve(request, engineDescriptor, classFilter); - filter(engineDescriptor, classFilter); - pruneTree(engineDescriptor); - } - - private void resolve(EngineDiscoveryRequest request, TestDescriptor engineDescriptor, ClassFilter classFilter) { - - JavaElementsResolver javaElementsResolver = createJavaElementsResolver(request.getConfigurationParameters(), - engineDescriptor, classFilter); - - request.getSelectorsByType(ClasspathRootSelector.class).forEach(javaElementsResolver::resolveClasspathRoot); - request.getSelectorsByType(ModuleSelector.class).forEach(javaElementsResolver::resolveModule); - request.getSelectorsByType(PackageSelector.class).forEach(javaElementsResolver::resolvePackage); - request.getSelectorsByType(ClassSelector.class).forEach(javaElementsResolver::resolveClass); - request.getSelectorsByType(MethodSelector.class).forEach(javaElementsResolver::resolveMethod); - request.getSelectorsByType(UniqueIdSelector.class).forEach(javaElementsResolver::resolveUniqueId); - } - - private void filter(TestDescriptor engineDescriptor, ClassFilter classFilter) { - new DiscoveryFilterApplier().applyClassNamePredicate(classFilter::match, engineDescriptor); - } + private static final EngineDiscoveryRequestResolver resolver = EngineDiscoveryRequestResolver + .builder() // + .addClassContainerSelectorResolver(IsBenchmarkClass.INSTANCE) + .addSelectorResolver(ctx -> new BenchmarkContainerResolver(ctx.getClassNameFilter())) // + .addSelectorResolver(new BenchmarkMethodResolver()) // + .addSelectorResolver(new BenchmarkFixtureResolver()) // + .build(); - private void pruneTree(TestDescriptor rootDescriptor) { - rootDescriptor.accept(TestDescriptor::prune); - } - - private JavaElementsResolver createJavaElementsResolver(ConfigurationParameters configurationParameters, - TestDescriptor engineDescriptor, ClassFilter classFilter) { - return new JavaElementsResolver(engineDescriptor, classFilter, ElementResolvers.getResolvers()); + public void resolveSelectors(EngineDiscoveryRequest request, TestDescriptor engineDescriptor) { + resolver.resolve(request, engineDescriptor); } } diff --git a/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/ElementResolver.java b/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/ElementResolver.java deleted file mode 100644 index 2a87d8e..0000000 --- a/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/ElementResolver.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2018 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 - * - * http://www.eclipse.org/legal/epl-v20.html - */ -package jmh.mbr.junit5.discovery; - -import java.lang.reflect.AnnotatedElement; -import java.util.Optional; -import java.util.Set; - -import org.junit.platform.engine.TestDescriptor; -import org.junit.platform.engine.UniqueId; - -interface ElementResolver { - - /** - * Return a set of {@link TestDescriptor TestDescriptors} that can be resolved by this resolver. - *

- * Returned set must be empty if {@code element} cannot be resolved. - */ - Set resolveElement(AnnotatedElement element, TestDescriptor parent); - - /** - * Return an optional {@link TestDescriptor}. - *

- * Return {@code Optional.empty()} if {@code segment} cannot be resolved. - */ - Optional resolveUniqueId(UniqueId.Segment segment, TestDescriptor parent); - -} diff --git a/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/ElementResolvers.java b/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/ElementResolvers.java deleted file mode 100644 index 3fb8c72..0000000 --- a/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/ElementResolvers.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2018 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 - * - * http://www.eclipse.org/legal/epl-v20.html - */ -package jmh.mbr.junit5.discovery; - -import java.util.LinkedHashSet; -import java.util.Set; - -/** - * Utility to obtain {@link ElementResolver}s. - */ -class ElementResolvers { - - /** - * @return a {@link Set} of known {@link ElementResolver}s. - */ - public static Set getResolvers() { - - Set resolvers = new LinkedHashSet<>(); - resolvers.add(new BenchmarkContainerResolver()); - resolvers.add(new BenchmarkMethodResolver()); - resolvers.add(new BenchmarkFixtureResolver()); - - return resolvers; - } -} diff --git a/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/JavaElementsResolver.java b/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/JavaElementsResolver.java deleted file mode 100644 index 92d50e9..0000000 --- a/microbenchmark-runner-junit5/src/main/java/jmh/mbr/junit5/discovery/JavaElementsResolver.java +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright 2018 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 - * - * http://www.eclipse.org/legal/epl-v20.html - */ -package jmh.mbr.junit5.discovery; - -import jmh.mbr.junit5.MicrobenchmarkEngine; -import jmh.mbr.junit5.descriptor.BenchmarkClassDescriptor; -import jmh.mbr.junit5.descriptor.ParametrizedBenchmarkMethodDescriptor; -import jmh.mbr.junit5.discovery.predicates.IsBenchmarkMethod; - -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.Collections; -import java.util.Deque; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.stream.Collectors; - -import org.junit.platform.commons.logging.Logger; -import org.junit.platform.commons.logging.LoggerFactory; -import org.junit.platform.commons.support.scanning.ClassFilter; -import org.junit.platform.commons.util.BlacklistedExceptions; -import org.junit.platform.commons.util.ReflectionUtils; -import org.junit.platform.commons.util.StringUtils; -import org.junit.platform.engine.TestDescriptor; -import org.junit.platform.engine.UniqueId; -import org.junit.platform.engine.UniqueId.Segment; -import org.junit.platform.engine.discovery.ClassSelector; -import org.junit.platform.engine.discovery.ClasspathRootSelector; -import org.junit.platform.engine.discovery.MethodSelector; -import org.junit.platform.engine.discovery.ModuleSelector; -import org.junit.platform.engine.discovery.PackageSelector; -import org.junit.platform.engine.discovery.UniqueIdSelector; - -/** - * Resolve {@link TestDescriptor} by traversing classes and methods and attaching these to the root - * {@link TestDescriptor test engine descriptor}. - */ -class JavaElementsResolver { - - private static final Logger logger = LoggerFactory.getLogger(JavaElementsResolver.class); - - private final TestDescriptor engineDescriptor; - private final ClassFilter classFilter; - private final Set resolvers; - - JavaElementsResolver(TestDescriptor engineDescriptor, ClassFilter classFilter, Set resolvers) { - this.engineDescriptor = engineDescriptor; - this.classFilter = classFilter; - this.resolvers = resolvers; - } - - void resolveClasspathRoot(ClasspathRootSelector selector) { - try { - ReflectionUtils.findAllClassesInClasspathRoot(selector.getClasspathRoot(), this.classFilter) - .forEach(this::resolveClass); - } catch (Throwable t) { - BlacklistedExceptions.rethrowIfBlacklisted(t); - logger.debug(t, - () -> String.format("Failed to resolve classes in classpath root '%s'.", selector.getClasspathRoot())); - } - } - - void resolveModule(ModuleSelector selector) { - try { - ReflectionUtils.findAllClassesInModule(selector.getModuleName(), this.classFilter).forEach(this::resolveClass); - } catch (Throwable t) { - BlacklistedExceptions.rethrowIfBlacklisted(t); - logger.debug(t, () -> String.format("Failed to resolve classes in module '%s'.", selector.getModuleName())); - } - } - - void resolvePackage(PackageSelector selector) { - try { - ReflectionUtils.findAllClassesInPackage(selector.getPackageName(), this.classFilter).forEach(this::resolveClass); - } catch (Throwable t) { - BlacklistedExceptions.rethrowIfBlacklisted(t); - logger.debug(t, () -> String.format("Failed to resolve classes in package '%s'.", selector.getPackageName())); - } - } - - void resolveClass(ClassSelector selector) { - // Even though resolveClass(Class) has its own similar try-catch block, the - // try-catch block is necessary here as well since ClassSelector#getJavaClass() - // may throw an exception. - try { - resolveClass(selector.getJavaClass()); - } catch (Throwable t) { - BlacklistedExceptions.rethrowIfBlacklisted(t); - logger.debug(t, () -> String.format("Class '%s' could not be resolved.", selector.getClassName())); - } - } - - private void resolveClass(Class testClass) { - try { - Set resolvedDescriptors = resolveContainerWithParents(testClass); - resolvedDescriptors.forEach(this::resolveChildren); - - if (resolvedDescriptors.isEmpty()) { - logger.debug(() -> String.format("Class '%s' could not be resolved.", StringUtils.nullSafeToString(testClass))); - } - } catch (Throwable t) { - BlacklistedExceptions.rethrowIfBlacklisted(t); - logger.debug(t, - () -> String.format("Class '%s' could not be resolved.", StringUtils.nullSafeToString(testClass))); - } - } - - void resolveMethod(MethodSelector selector) { - - try { - Class testClass = selector.getJavaClass(); - Method testMethod = selector.getJavaMethod(); - - Set potentialParents = resolveContainerWithParents(testClass); - Set resolvedDescriptors = resolveForAllParents(testMethod, potentialParents); - resolvedDescriptors.forEach(this::resolveChildren); - - if (resolvedDescriptors.isEmpty()) { - logger.debug(() -> String.format("Method '%s' could not be resolved.", testMethod.toGenericString())); - } - } catch (Throwable t) { - BlacklistedExceptions.rethrowIfBlacklisted(t); - logger.debug(t, () -> String.format("Method '%s' in class '%s' could not be resolved.", selector.getMethodName(), - selector.getClassName())); - } - } - - void resolveUniqueId(UniqueIdSelector selector) { - - UniqueId uniqueId = selector.getUniqueId(); - - // Ignore Unique IDs from other test engines. - if (MicrobenchmarkEngine.ENGINE_ID.equals(uniqueId.getEngineId().orElse(null))) { - try { - Deque resolvedDescriptors = resolveAllSegments(uniqueId); - handleResolvedDescriptorsForUniqueId(uniqueId, resolvedDescriptors); - } catch (Throwable t) { - BlacklistedExceptions.rethrowIfBlacklisted(t); - logger.warn(t, () -> String.format("Unique ID '%s' could not be resolved.", selector.getUniqueId())); - } - } - } - - private Set resolveContainerWithParents(Class testClass) { - return resolveForAllParents(testClass, Collections.singleton(this.engineDescriptor)); - } - - /** - * Attempt to resolve all segments for the supplied unique ID. - */ - private Deque resolveAllSegments(UniqueId uniqueId) { - - List segments = uniqueId.getSegments(); - Deque resolvedDescriptors = new LinkedList<>(); - resolvedDescriptors.addFirst(this.engineDescriptor); - - for (int index = 1; index < segments.size() && resolvedDescriptors.size() == index; index++) { - Segment segment = segments.get(index); - TestDescriptor parent = resolvedDescriptors.getLast(); - UniqueId partialUniqueId = parent.getUniqueId().append(segment); - - Optional resolvedDescriptor = findTestDescriptorByUniqueId(partialUniqueId); - if (!resolvedDescriptor.isPresent()) { - resolvedDescriptor = this.resolvers.stream().map(resolver -> resolver.resolveUniqueId(segment, parent)) - .filter(Optional::isPresent).map(Optional::get).findFirst(); - resolvedDescriptor.ifPresent(parent::addChild); - } - resolvedDescriptor.ifPresent(resolvedDescriptors::addLast); - } - return resolvedDescriptors; - } - - private void handleResolvedDescriptorsForUniqueId(UniqueId uniqueId, Deque resolvedDescriptors) { - List segments = uniqueId.getSegments(); - int numSegmentsToResolve = segments.size() - 1; - int numSegmentsResolved = resolvedDescriptors.size() - 1; - - if (numSegmentsResolved == 0) { - logger.warn(() -> String.format("Unique ID '%s' could not be resolved.", uniqueId)); - } else if (numSegmentsResolved != numSegmentsToResolve) { - logger.warn(() -> { - List unresolved = segments.subList(1, segments.size()); // Remove engine ID - unresolved = unresolved.subList(numSegmentsResolved, unresolved.size()); // Remove resolved segments - return String.format("Unique ID '%s' could only be partially resolved. " - + "All resolved segments will be executed; however, the " + "following segments could not be resolved: %s", - uniqueId, unresolved); - }); - } else { - resolveChildren(resolvedDescriptors.getLast()); - } - } - - private Set resolveForAllParents(AnnotatedElement element, Set potentialParents) { - return potentialParents.stream().flatMap(it -> resolve(element, it).stream()).collect(Collectors.toSet()); - } - - private void resolveChildren(TestDescriptor descriptor) { - - if (descriptor instanceof BenchmarkClassDescriptor) { - - Class testClass = ((BenchmarkClassDescriptor) descriptor).getJavaClass(); - resolveContainedMethods(descriptor, testClass, this::resolve); - } - - if (descriptor instanceof ParametrizedBenchmarkMethodDescriptor) { - - Method benchmarkMethod = ((ParametrizedBenchmarkMethodDescriptor) descriptor).getMethod(); - resolve(benchmarkMethod, descriptor); - } - } - - private void resolveContainedMethods(TestDescriptor containerDescriptor, Class testClass, - BiConsumer fixtureResolver) { - - List benchmarkMethodCandidates = ReflectionUtils.findMethods(testClass, IsBenchmarkMethod.INSTANCE); - benchmarkMethodCandidates.forEach(it -> { - Set methodDescriptors = resolve(it, containerDescriptor); - methodDescriptors.forEach(methodDescriptor -> fixtureResolver.accept(it, methodDescriptor)); - }); - } - - private Set resolve(AnnotatedElement element, TestDescriptor parent) { - - return this.resolvers.stream() // - .map(resolver -> tryToResolveWithResolver(element, parent, resolver)) // - .filter(testDescriptors -> !testDescriptors.isEmpty()) // - .flatMap(Collection::stream) // - .collect(Collectors.toSet()); - } - - private Set tryToResolveWithResolver(AnnotatedElement element, TestDescriptor parent, - ElementResolver resolver) { - - Set resolvedDescriptors = resolver.resolveElement(element, parent); - Set result = new LinkedHashSet<>(); - - resolvedDescriptors.forEach(testDescriptor -> { - Optional existingTestDescriptor = findTestDescriptorByUniqueId(testDescriptor.getUniqueId()); - if (existingTestDescriptor.isPresent()) { - result.add(existingTestDescriptor.get()); - } else { - parent.addChild(testDescriptor); - result.add(testDescriptor); - } - }); - - return result; - } - - @SuppressWarnings("unchecked") - private Optional findTestDescriptorByUniqueId(UniqueId uniqueId) { - return (Optional) this.engineDescriptor.findByUniqueId(uniqueId); - } -}