Skip to content

Commit a177e7c

Browse files
committed
createTestDescriptor has been rewritten to always attach qualified name
isSuitePredicate now rule out object & inner class all test passed all integration test passed organise imports upgrade scala versions # Conflicts: # build.sbt
1 parent e6280f7 commit a177e7c

File tree

14 files changed

+399
-24
lines changed

14 files changed

+399
-24
lines changed

examples/gradle-example/gradlew

100755100644
File mode changed.

examples/gradle-kotlin-dsl-example/gradlew

100755100644
File mode changed.

src/main/scala/org/scalatestplus/junit5/EngineExecutionListenerReporter.scala

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,23 @@ private[junit5] class EngineExecutionListenerReporter(
5454
testName: String,
5555
locationOpt: Option[Location]
5656
): ScalaTestDescriptor = {
57-
val uniqueId = clzDesc.theUniqueId.append("test", testName)
58-
new ScalaTestDescriptor(uniqueId, testName, locationOpt)
57+
58+
val qualifiedName = s"$suiteName - $testName"
59+
if (suiteId.==(clzDesc.suiteClass.getName)) {
60+
val uniqueId = clzDesc.theUniqueId.append("test", testName)
61+
62+
new ScalaTestDescriptor(uniqueId, qualifiedName, locationOpt)
63+
} else {
64+
65+
val qualifier = suiteId.stripPrefix(clzDesc.suiteClass.getName)
66+
// nested test case, demands a qualified name
67+
val uniqueId = clzDesc.theUniqueId
68+
.append("nested-class", qualifier)
69+
.append("test", testName)
70+
71+
new ScalaTestDescriptor(uniqueId, qualifiedName, locationOpt)
72+
}
73+
5974
}
6075

6176
override def apply(event: Event): Unit = {
@@ -76,7 +91,10 @@ private[junit5] class EngineExecutionListenerReporter(
7691
threadName,
7792
timeStamp
7893
) =>
79-
val testDesc = createTestDescriptor(suiteId, suiteName, suiteClassName, testName, location)
94+
95+
val testDesc: ScalaTestDescriptor =
96+
createTestDescriptor(suiteId, suiteName, suiteClassName, testName, location)
97+
8098
clzDesc.addChild(testDesc)
8199
listener.dynamicTestRegistered(testDesc)
82100
listener.executionStarted(testDesc)
@@ -100,8 +118,8 @@ private[junit5] class EngineExecutionListenerReporter(
100118
threadName,
101119
timeStamp
102120
) =>
103-
val throwableOrNull = throwable.orNull
104121
val testDesc = createTestDescriptor(suiteId, suiteName, suiteClassName, testName, location)
122+
val throwableOrNull = throwable.orNull
105123
listener.executionFinished(testDesc, TestExecutionResult.failed(throwableOrNull))
106124

107125
case TestSucceeded(
@@ -140,7 +158,7 @@ private[junit5] class EngineExecutionListenerReporter(
140158
listener.executionSkipped(testDesc, "Test ignored.")
141159

142160
case TestCanceled(
143-
ordering,
161+
ordinal,
144162
message,
145163
suiteName,
146164
suiteId,

src/main/scala/org/scalatestplus/junit5/JUnitExecutionListener.scala

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,14 @@
1515
*/
1616
package org.scalatestplus.junit5;
1717

18+
import org.junit.platform.engine.TestExecutionResult
1819
import org.junit.platform.launcher.{TestExecutionListener, TestIdentifier}
19-
import org.junit.platform.engine.{EngineExecutionListener, TestDescriptor, TestExecutionResult}
20-
import org.scalatest.{Reporter, StatefulStatus, Tracker}
21-
import org.scalatest.events.{
22-
MotionToSuppress,
23-
SeeStackDepthException,
24-
TestFailed,
25-
TestIgnored,
26-
TestStarting,
27-
TestSucceeded,
28-
TopOfMethod
29-
}
20+
import org.scalatest.events._
3021
import org.scalatest.exceptions.PayloadField
31-
import JUnitHelper.getIndentedTextForTest
22+
import org.scalatest.{Reporter, StatefulStatus, Tracker}
23+
import org.scalatestplus.junit5.JUnitHelper.getIndentedTextForTest
3224

33-
import java.util.Collections
34-
import java.util.HashSet
25+
import java.util.{Collections, HashSet}
3526
import java.util.regex.Pattern
3627

3728
private[junit5] class JUnitExecutionListener(

src/main/scala/org/scalatestplus/junit5/ScalaTestEngine.scala

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,17 @@ class ScalaTestEngine extends org.junit.platform.engine.TestEngine {
7474

7575
val isSuitePredicate =
7676
new java.util.function.Predicate[Class[_]]() {
77-
def test(t: Class[_]): Boolean =
78-
classOf[org.scalatest.Suite].isAssignableFrom(t) &&
79-
!Modifier.isAbstract(t.getModifiers) &&
80-
JUnitHelper.checkForPublicNoArgConstructor(t)
77+
def test(t: Class[_]): Boolean = {
78+
val isSuite = classOf[org.scalatest.Suite].isAssignableFrom(t)
79+
val notAbstract = !Modifier.isAbstract(t.getModifiers)
80+
val notObject =
81+
!t.getCanonicalName.endsWith("$") // must not be an object
82+
83+
val notInner = t.getEnclosingClass == null
84+
val canInit = JUnitHelper.checkForPublicNoArgConstructor(t)
85+
86+
isSuite && notAbstract && notObject && notInner && canInit
87+
}
8188
}
8289

8390
def classDescriptorFunction(aClass: Class[_]) =
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package org.scalatestplus.junit5
2+
3+
import org.junit.platform.engine.discovery.DiscoverySelectors.selectClass
4+
import org.junit.platform.launcher.core.{LauncherDiscoveryRequestBuilder, LauncherFactory}
5+
import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach, Suite, funspec}
6+
import org.scalatestplus.junit5.integration.listener.{JUnitListener, ScalaTestListener}
7+
import org.scalatestplus.junit5.integration.NestedFixture
8+
9+
import java.lang.reflect.Modifier
10+
import java.nio.file.{Files, Path, Paths}
11+
import java.util.jar.JarFile
12+
13+
class IntegrationTests extends funspec.AnyFunSpec with BeforeAndAfterAll with BeforeAndAfterEach {
14+
15+
import scala.collection.JavaConverters._
16+
17+
var scalaTestEngineProperty: Option[String] = None
18+
19+
override def beforeAll(): Unit = {
20+
scalaTestEngineProperty = Option(System.clearProperty("org.scalatestplus.junit5.ScalaTestEngine.disabled"))
21+
}
22+
23+
override def afterAll(): Unit = {
24+
scalaTestEngineProperty.foreach(System.setProperty("org.scalatestplus.junit5.ScalaTestEngine.disabled", _))
25+
}
26+
27+
lazy val integrationDirPath: Path = {
28+
val classPathRoot = this.getClass.getProtectionDomain.getCodeSource.getLocation
29+
30+
val root = Paths.get(classPathRoot.toURI)
31+
32+
val result = root.resolve("org/scalatestplus/junit5/integration")
33+
34+
println(s"integration path set to $result")
35+
result
36+
}
37+
38+
private def findTestClasses(path: Path): Set[Class[_]] = {
39+
val suiteClass = classOf[Suite]
40+
41+
def isTestClass(cls: Class[_]): Boolean = {
42+
suiteClass.isAssignableFrom(cls) && !Modifier.isAbstract(cls.getModifiers) && !cls.isInterface
43+
}
44+
45+
val classPathRoot = Paths.get(getClass.getProtectionDomain.getCodeSource.getLocation.toURI)
46+
47+
val classNames = if (Files.isDirectory(path)) {
48+
Files
49+
.list(path)
50+
.iterator()
51+
.asScala
52+
.filter(p => p.toString.endsWith(".class"))
53+
.map(p => classPathRoot.relativize(p).toString.replace('/', '.').dropRight(".class".length))
54+
.toSet
55+
} else if (path.toString.toLowerCase.endsWith(".jar")) {
56+
val jarFile = new JarFile(path.toFile)
57+
try {
58+
jarFile
59+
.entries()
60+
.asScala
61+
.filter(e => e.getName.endsWith(".class"))
62+
.map(_.getName.replace('/', '.').dropRight(".class".length))
63+
.toSet
64+
} finally {
65+
jarFile.close()
66+
}
67+
} else {
68+
Set.empty[String]
69+
}
70+
71+
classNames
72+
.filterNot { name =>
73+
name.contains("$")
74+
}
75+
.flatMap { className =>
76+
try {
77+
val classLoader = getClass.getClassLoader
78+
val cls = Class.forName(className, false, classLoader)
79+
val result: Option[Class[_]] = Some(cls).filter(isTestClass)
80+
result
81+
} catch {
82+
case _: Throwable => None
83+
}
84+
}
85+
}
86+
87+
lazy val integrationTests: Set[Class[_]] = findTestClasses(integrationDirPath);
88+
89+
override def beforeEach(): Unit = {
90+
ScalaTestListener.clear()
91+
JUnitListener.clear()
92+
}
93+
94+
describe("ScalaTest runner and JUnit runner should discovery & run identical tests") {
95+
96+
describe("in classes") {
97+
98+
integrationTests.foreach { clz =>
99+
val clzName = clz.getName
100+
it(s"${clz.getSimpleName}") {
101+
102+
// with ScalaTest runner
103+
104+
org.scalatest.tools.Runner.run(
105+
Array(
106+
"-R",
107+
integrationDirPath.toString,
108+
"-s",
109+
clzName,
110+
"-C",
111+
classOf[ScalaTestListener].getCanonicalName
112+
// "-oN"
113+
)
114+
)
115+
116+
// with JUnit 5 runner
117+
{
118+
val launcher = LauncherFactory.create()
119+
120+
val discoveryRequest = LauncherDiscoveryRequestBuilder.request
121+
.selectors(
122+
selectClass(clzName)
123+
)
124+
.build()
125+
126+
launcher.execute(discoveryRequest, new JUnitListener())
127+
}
128+
129+
JUnitListener.startedShouldBe(ScalaTestListener.started.mkString("\n"))
130+
JUnitListener.finishedShouldBe(ScalaTestListener.finished.mkString("\n"))
131+
}
132+
}
133+
}
134+
135+
ignore("in packages") { // TODO this doesn't work
136+
137+
val pkg = classOf[NestedFixture].getPackage.getName
138+
org.scalatest.tools.Runner.run(
139+
Array(
140+
"-R",
141+
integrationDirPath.toString,
142+
"-w",
143+
pkg,
144+
"-C",
145+
classOf[ScalaTestListener].getCanonicalName
146+
// "-oN"
147+
)
148+
)
149+
}
150+
}
151+
152+
}

src/test/scala/org/scalatestplus/junit5/ScalaTestEngineSpec.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package org.scalatestplus.junit5
22

33
import org.junit.platform.engine.UniqueId
4-
import org.junit.platform.engine.discovery.ClasspathRootSelector
54
import org.junit.platform.engine.discovery.DiscoverySelectors.{selectClasspathRoots, selectPackage}
65
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request
76
import org.scalatest.{BeforeAndAfterAll, funspec}
87
import org.scalatestplus.junit5.helpers.HappySuite
8+
import org.scalatestplus.junit5.integration.PlainFixture
99

1010
import java.nio.file.{Files, Paths}
1111
import scala.collection.JavaConverters._
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.scalatestplus.junit5.integration
2+
3+
import org.scalatest.funspec.AnyFunSpec
4+
5+
class DescribeFixture extends AnyFunSpec {
6+
7+
describe("group") {
8+
9+
it("a") {}
10+
11+
it("b") {}
12+
}
13+
14+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.scalatestplus.junit5.integration
2+
3+
import org.scalatest.Suite
4+
import org.scalatest.funspec.AnyFunSpec
5+
6+
import scala.collection.immutable
7+
8+
object NestedFixture {
9+
10+
class Inner extends AnyFunSpec {
11+
it("a") {}
12+
}
13+
14+
object I1 extends Inner
15+
}
16+
class NestedFixture extends AnyFunSpec {
17+
18+
val i2 = new NestedFixture.Inner {
19+
override def suiteName: String = "i2"
20+
}
21+
22+
override def nestedSuites: immutable.IndexedSeq[Suite] = {
23+
24+
val i3 = new NestedFixture.Inner {
25+
override def suiteName: String = "i3"
26+
}
27+
28+
immutable.IndexedSeq[Suite](
29+
NestedFixture.I1, // object
30+
i2, // member
31+
i3, // local instance
32+
new NestedFixture.Inner() {
33+
override def suiteName: String = "i4"
34+
} // ad-hoc
35+
)
36+
}
37+
38+
it("b") {}
39+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.scalatestplus.junit5.integration
2+
3+
import org.scalatest.funspec.AnyFunSpec
4+
import org.scalatestplus.junit5.integration.listener
5+
6+
class PlainFixture extends AnyFunSpec {
7+
8+
it("a") {}
9+
10+
it("b") {}
11+
}

0 commit comments

Comments
 (0)