Skip to content

Commit ad5ec23

Browse files
committed
Merge branch 'jakobcodes/method-level-test-filtering' of https://github.com/jakobcodes/scalatestplus-junit
2 parents 164ddf3 + f996229 commit ad5ec23

File tree

2 files changed

+81
-8
lines changed

2 files changed

+81
-8
lines changed

src/main/scala/org/scalatestplus/junit/JUnitRunner.scala

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,16 @@
1616
*/
1717
package org.scalatestplus.junit
1818

19-
import org.scalatest._
19+
import org.scalatest.{Args, ConfigMap, DynaTags, Stopper, Suite, Tracker, Filter}
2020
import org.junit.runner.notification.RunNotifier
2121
import org.junit.runner.notification.Failure
2222
import org.junit.runner.Description
2323
import org.junit.runner.manipulation.{Filter => TestFilter, Filterable, NoTestsRemainException}
2424

25+
import java.util.concurrent.ConcurrentHashMap
26+
import scala.collection.JavaConverters._
27+
import scala.collection.mutable
28+
2529
/*
2630
I think that Stopper really should be a no-op, like it is, because the user has
2731
no way to stop it. This is wierd, because it will call nested suites. So the tests
@@ -71,6 +75,8 @@ final class JUnitRunner(suiteClass: java.lang.Class[_ <: Suite]) extends org.jun
7175
*/
7276
val getDescription = createDescription(suiteToRun)
7377

78+
private val excludedTests: mutable.Set[String] = ConcurrentHashMap.newKeySet[String]().asScala
79+
7480
private def createDescription(suite: Suite): Description = {
7581
val description = Description.createSuiteDescription(suite.getClass)
7682
// If we don't add the testNames and nested suites in, we get
@@ -96,12 +102,29 @@ final class JUnitRunner(suiteClass: java.lang.Class[_ <: Suite]) extends org.jun
96102
*/
97103
def run(notifier: RunNotifier): Unit = {
98104
try {
105+
val includedTests: Set[String] = suiteToRun.testNames.diff(excludedTests)
106+
val testTags: Map[String, Map[String, Set[String]]] = Map(
107+
suiteToRun.suiteId ->
108+
includedTests.map(test => test -> Set("org.scalatest.Selected")).toMap
109+
)
110+
val filter = Filter(
111+
tagsToInclude = Some(Set("org.scalatest.Selected")),
112+
dynaTags = DynaTags(suiteTags = Map.empty, testTags = testTags)
113+
)
99114
// TODO: What should this Tracker be?
100-
suiteToRun.run(None, Args(new RunNotifierReporter(notifier),
101-
Stopper.default, Filter(), ConfigMap.empty, None,
102-
new Tracker))
103-
}
104-
catch {
115+
suiteToRun.run(
116+
None,
117+
Args(
118+
new RunNotifierReporter(notifier),
119+
Stopper.default,
120+
filter,
121+
ConfigMap.empty,
122+
None,
123+
new Tracker,
124+
Set.empty
125+
)
126+
)
127+
} catch {
105128
case e: Exception =>
106129
notifier.fireTestFailure(new Failure(getDescription, e))
107130
}
@@ -113,10 +136,16 @@ final class JUnitRunner(suiteClass: java.lang.Class[_ <: Suite]) extends org.jun
113136
*
114137
* @return the expected number of tests that will run when this suite is run
115138
*/
116-
override def testCount() = suiteToRun.expectedTestCount(Filter())
139+
override def testCount(): Int = suiteToRun.expectedTestCount(Filter())
117140

141+
@throws(classOf[NoTestsRemainException])
118142
override def filter(filter: TestFilter): Unit = {
119-
if (!filter.shouldRun(getDescription)) throw new NoTestsRemainException
143+
val children = getDescription.getChildren.asScala
144+
excludedTests ++= children
145+
.filterNot(filter.shouldRun)
146+
.map(_.getMethodName)
147+
148+
if (children.isEmpty) throw new NoTestsRemainException
120149
}
121150

122151
}

src/test/scala/org/scalatestplus/junit/JUnitRunnerSuite.scala

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,30 @@ package org.scalatestplus.junit {
103103
assert(1 === 1)
104104
}
105105
}
106+
107+
class MethodExclusionFilter(methodNamePattern: String) extends TestFilter {
108+
override def shouldRun(description: Description): Boolean = {
109+
!description.getMethodName().contains(methodNamePattern)
110+
}
111+
112+
override def describe(): String = s"Excludes tests with method names containing: '$methodNamePattern'"
113+
}
114+
115+
@RunWith(classOf[JUnitRunner])
116+
class MethodFilterTargetSuite extends funsuite.AnyFunSuite {
117+
118+
test("JUnit ran this OK!") {
119+
assert(1 === 1)
120+
}
121+
122+
test("should pass filter and execute") {
123+
assert(2 === 2)
124+
}
125+
126+
test("should be filtered out") {
127+
assert(3 === 5)
128+
}
129+
}
106130
}
107131

108132
import org.junit.runner.Description
@@ -112,6 +136,7 @@ package org.scalatestplus.junit {
112136
import org.scalatestplus.junit.helpers.EasySuite
113137
import org.scalatestplus.junit.helpers.KerblooeySuite
114138
import org.scalatestplus.junit.helpers.{FilteredInSuite, FilteredOutSuite, NameFilter}
139+
import org.scalatestplus.junit.helpers.{MethodExclusionFilter, MethodFilterTargetSuite}
115140
import scala.util.Try
116141

117142
class JUnitRunnerSuite extends funsuite.AnyFunSuite {
@@ -190,5 +215,24 @@ package org.scalatestplus.junit {
190215
assert(runNotifier.ran.head.getDisplayName ===
191216
"JUnit ran this OK!(org.scalatestplus.junit.helpers.FilteredInSuite)")
192217
}
218+
219+
test("Should execute only methods that don't match the filter pattern") {
220+
class ExecutedTestsNotifier extends RunNotifier {
221+
var executedTests: List[Description] = Nil
222+
223+
override def fireTestFinished(description: Description): Unit = {
224+
executedTests = description :: executedTests
225+
}
226+
}
227+
val runNotifier = new ExecutedTestsNotifier()
228+
val runner = new JUnitRunner(classOf[MethodFilterTargetSuite])
229+
230+
val excludeMethodFilter = new MethodExclusionFilter("should be filtered out")
231+
232+
runner.filter(excludeMethodFilter)
233+
runner.run(runNotifier)
234+
235+
assert(runNotifier.executedTests.size === 2) // Verifies that only 2 tests ran (one was filtered out)
236+
}
193237
}
194238
}

0 commit comments

Comments
 (0)