diff --git a/build.gradle.kts b/build.gradle.kts index f414b0c..694659a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -56,8 +56,10 @@ dependencies { compileOnly("org.scalatest:scalatest_2.11:3.3.0-SNAP3") testImplementation("org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion") - testImplementation("org.junit.platform:junit-platform-launcher:$junitPlatformVersion") testImplementation("org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion") + testImplementation("org.junit.jupiter:junit-jupiter-params:$junitJupiterVersion") + testImplementation("org.junit.platform:junit-platform-launcher:$junitPlatformVersion") + testImplementation("org.junit.platform:junit-platform-testkit:$junitPlatformVersion") testImplementation("org.junit.platform:junit-platform-engine:1.6.0") testImplementation("org.scalatest:scalatest_$testScalaLibraryVersion:3.3.0-SNAP3") testImplementation("org.scala-lang:scala-library:$testScalaVersion") diff --git a/src/main/java/co/helmethair/scalatest/ScalatestEngine.java b/src/main/java/co/helmethair/scalatest/ScalatestEngine.java index df516f6..18ac8a4 100644 --- a/src/main/java/co/helmethair/scalatest/ScalatestEngine.java +++ b/src/main/java/co/helmethair/scalatest/ScalatestEngine.java @@ -4,15 +4,21 @@ import co.helmethair.scalatest.reporter.JUnitReporter; import co.helmethair.scalatest.runtime.Discovery; import co.helmethair.scalatest.runtime.Executor; -import org.junit.platform.engine.*; +import org.junit.platform.engine.ConfigurationParameters; +import org.junit.platform.engine.EngineDiscoveryRequest; +import org.junit.platform.engine.ExecutionRequest; +import org.junit.platform.engine.TestDescriptor; +import org.junit.platform.engine.TestEngine; +import org.junit.platform.engine.UniqueId; import org.junit.platform.engine.discovery.ClassSelector; -import org.scalatest.DoNotDiscover; -import org.scalatest.Suite; +import org.junit.platform.engine.discovery.UniqueIdSelector; -import java.lang.reflect.Modifier; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static co.helmethair.scalatest.descriptor.ScalatestDescriptor.SUITE_TYPE; public class ScalatestEngine implements TestEngine { public static final String ID = "scalatest"; @@ -29,9 +35,12 @@ public String getId() { public TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) { ScalatestEngineDescriptor engineDescriptor = new ScalatestEngineDescriptor(uniqueId, ID); - List> classes = discoverClassSelectors(discoveryRequest); - - return runtime.discover(engineDescriptor, classes, Thread.currentThread().getContextClassLoader()); + return runtime.discover(engineDescriptor, + Stream.concat( + discoverClassSelectors(discoveryRequest).stream(), + discoverUniqueIdSelectors(discoveryRequest).stream() + ).collect(Collectors.toSet()), + Thread.currentThread().getContextClassLoader()); } @Override @@ -46,14 +55,28 @@ public void execute(ExecutionRequest executionRequest) { executor.executeTest(executionRequest.getRootTestDescriptor(), reporter); } - @SuppressWarnings("unchecked") - private List> discoverClassSelectors(EngineDiscoveryRequest dicoveryRequest) { - return dicoveryRequest.getSelectorsByType(ClassSelector.class).stream().map(ClassSelector::getJavaClass) - .filter(c -> !(c.getEnclosingMethod() != null //only local or anonymous classes have an enclosing method - || c.isSynthetic() - || Modifier.isAbstract(c.getModifiers()) - || c.getAnnotation(DoNotDiscover.class) != null) - && Suite.class.isAssignableFrom(c) - ).map(c -> ((Class) c)).collect(Collectors.toList()); + private List discoverUniqueIdSelectors(EngineDiscoveryRequest discoveryRequest) { + return discoveryRequest.getSelectorsByType(UniqueIdSelector.class).stream() + .map(this::getSuite) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toList()); + } + + private Optional getSuite(UniqueIdSelector u) { + UniqueId uniqueId = u.getUniqueId(); + if (uniqueId.hasPrefix(UniqueId.forEngine(ID))) { + UniqueId.Segment segment = uniqueId.getSegments().get(1); + if (SUITE_TYPE.equals(segment.getType())) { + return Optional.of(segment.getValue()); + } + } + return Optional.empty(); + } + + private List discoverClassSelectors(EngineDiscoveryRequest discoveryRequest) { + return discoveryRequest.getSelectorsByType(ClassSelector.class).stream() + .map(ClassSelector::getClassName) + .collect(Collectors.toList()); } } diff --git a/src/main/java/co/helmethair/scalatest/descriptor/ScalatestDescriptor.java b/src/main/java/co/helmethair/scalatest/descriptor/ScalatestDescriptor.java index d30fc87..74182d5 100644 --- a/src/main/java/co/helmethair/scalatest/descriptor/ScalatestDescriptor.java +++ b/src/main/java/co/helmethair/scalatest/descriptor/ScalatestDescriptor.java @@ -13,6 +13,7 @@ public abstract class ScalatestDescriptor implements TestDescriptor { final static UniqueId ENGINE_ID = UniqueId.forEngine(ScalatestEngine.ID); static final ConcurrentHashMap descriptorsById = new ConcurrentHashMap<>(); + public static final String SUITE_TYPE = "suite"; private final UniqueId id; private TestDescriptor parentDescriptor = null; private Set childDescriptors = new HashSet(); @@ -32,11 +33,11 @@ public static Optional findById(UniqueId uniqueId) { } static UniqueId testId(String suiteId, String testName) { - return ENGINE_ID.append("suite", suiteId).append("test", testName); + return ENGINE_ID.append(SUITE_TYPE, suiteId).append("test", testName); } static UniqueId containerId(String suiteId) { - return ENGINE_ID.append("suite", suiteId); + return ENGINE_ID.append(SUITE_TYPE, suiteId); } static UniqueId descriptorId(String suiteId, String testName) { diff --git a/src/main/java/co/helmethair/scalatest/runtime/Discovery.java b/src/main/java/co/helmethair/scalatest/runtime/Discovery.java index a54f73a..66e10ce 100644 --- a/src/main/java/co/helmethair/scalatest/runtime/Discovery.java +++ b/src/main/java/co/helmethair/scalatest/runtime/Discovery.java @@ -7,31 +7,56 @@ import co.helmethair.scalatest.scala.ScalaConversions; import org.junit.platform.engine.TestDescriptor; import org.junit.platform.engine.TestTag; +import org.scalatest.DoNotDiscover; import org.scalatest.Suite; import org.scalatest.TagAnnotation; import scala.Option; +import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.Collections; -import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import static java.util.Collections.*; + public class Discovery { - public ScalatestEngineDescriptor discover(ScalatestEngineDescriptor engineDescriptor, List> classes, ClassLoader classLoader) { + @SuppressWarnings("unchecked") + public ScalatestEngineDescriptor discover(ScalatestEngineDescriptor engineDescriptor, Set classes, ClassLoader classLoader) { classes.forEach(c -> { + Class aClass = null; try { - Suite suite = ((Suite) classLoader.loadClass(c.getName()).newInstance()); - addSuite(suite, engineDescriptor); + aClass = classLoader.loadClass(c); + if (isScalaSuite(aClass)) { + Suite suite = ((Suite) aClass.newInstance()); + addSuite(suite, engineDescriptor); + } } catch (Throwable e) { - addFailedInit(e, c, engineDescriptor); + if (aClass != null && Suite.class.isAssignableFrom(aClass)) { + addFailedInit(e, (Class) aClass, engineDescriptor); + } else { + addFailedInit(e, c, engineDescriptor); + } } }); return engineDescriptor; } + private boolean isScalaSuite(Class c) { + return !(c.getEnclosingMethod() != null //only local or anonymous classes have an enclosing method + || c.isSynthetic() + || Modifier.isAbstract(c.getModifiers()) + || c.getAnnotation(DoNotDiscover.class) != null) + && Suite.class.isAssignableFrom(c); + } + + private void addFailedInit(Throwable cause, String className, TestDescriptor parent) { + ScalatestFailedInitDescriptor failed = new ScalatestFailedInitDescriptor(cause, className, emptySet()); + linkChild(parent, failed); + } + private void addFailedInit(Throwable cause, Class clazz, TestDescriptor parent) { String className = clazz.getName(); ScalatestFailedInitDescriptor failed = new ScalatestFailedInitDescriptor(cause, className, extractTags(clazz)); @@ -74,6 +99,6 @@ private Set getTags(Suite scalasuite, String testName) { return ScalaConversions.setAsJavaSet(tagSetOption.get()).stream() .map(TestTag::create).collect(Collectors.toSet()); } - return Collections.emptySet(); + return emptySet(); } } diff --git a/src/test/java/co/helmethair/scalatest/UniqueIdTest.java b/src/test/java/co/helmethair/scalatest/UniqueIdTest.java new file mode 100644 index 0000000..52d3906 --- /dev/null +++ b/src/test/java/co/helmethair/scalatest/UniqueIdTest.java @@ -0,0 +1,26 @@ +package co.helmethair.scalatest; + +import co.helmethair.scalatest.helper.TestHelpers; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.platform.engine.discovery.DiscoverySelectors; +import org.junit.platform.testkit.engine.EngineTestKit; + +public class UniqueIdTest implements TestHelpers { + + @ParameterizedTest + @CsvSource({"[engine:scalatest]/[suite:tests.NestedTest], 2, 0", + // For now, it does not support really executing the selected test, it will execute the whole suite + "[engine:scalatest]/[suite:tests.NestedTest]/[test:nested test1], 2, 0", + "[engine:scalatest]/[foo:tests.NestedTest], 0, 0", + "[engine:scalatest]/[suite:tests.UnknownFoo], 0, 1", + }) + void handleUniqueId(String id, int successEvents, int failingEvents) { + EngineTestKit + .engine("scalatest") + .selectors(DiscoverySelectors.selectUniqueId(id)) + .execute() + .testEvents() + .assertStatistics(stats -> stats.succeeded(successEvents).failed(failingEvents)); + } +} diff --git a/src/test/java/co/helmethair/scalatest/helper/TestHelpers.java b/src/test/java/co/helmethair/scalatest/helper/TestHelpers.java index 70151f8..7073c79 100644 --- a/src/test/java/co/helmethair/scalatest/helper/TestHelpers.java +++ b/src/test/java/co/helmethair/scalatest/helper/TestHelpers.java @@ -57,7 +57,7 @@ public List getSelectorsByType(Class selecto if (selectorType == ClassSelector.class) { return ((List) Arrays.stream(classNames).map(DiscoverySelectors::selectClass).collect(Collectors.toList())); } - return null; + return Collections.emptyList(); } @Override