Skip to content

Commit d0f255d

Browse files
authored
Filter examples based on TestSelector (#1216)
Previously, the Specs2 runner would ignore the selectors that are passed via a `TaskDef`. With this patch, when a `TaskDef`'s selectors contains only `TestSelector`s, then only the examples whose name match the input selectors will be executed.
1 parent 34584de commit d0f255d

File tree

2 files changed

+95
-1
lines changed

2 files changed

+95
-1
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package org.specs2
2+
package runner
3+
4+
import sbt.testing.{Event, EventHandler, Logger, Selector, Status, SuiteSelector, TaskDef, TestSelector}
5+
6+
import scala.collection.mutable.ArrayBuffer
7+
8+
class SbtSelectorSpec extends Specification:
9+
def is = s2"""
10+
11+
An sbt runner executes the examples passed in the `TestSelector`s
12+
when there is a single `TestSelector` $singleTestSelector
13+
when there are 2 `TestSelector`s $twoTestSelectors
14+
run everything if there are other selectors $otherSelectors
15+
run nothing if there are no matches $noMatches
16+
regexes in test selectors are escaped $regexesAreEscaped
17+
"""
18+
19+
private def singleTestSelector =
20+
val wholeSuiteEvents = runWithSelectors(new SuiteSelector :: Nil)
21+
wholeSuiteEvents.length === 3
22+
val failEvents = wholeSuiteEvents.filter(_.status == Status.Failure)
23+
val singleExampleEvents = runWithSelectors(failEvents.map(_.selector()))
24+
25+
(failEvents must haveSize(1)) and
26+
(testName(failEvents.head) === "has a failing test") and
27+
(singleExampleEvents must haveSize(1)) and
28+
(singleExampleEvents.head.status() === Status.Failure)
29+
30+
private def twoTestSelectors =
31+
val events = runWithSelectors(
32+
List(new TestSelector("has a successful test"), new TestSelector("has a failing test"))
33+
)
34+
(events must haveSize(2)) and
35+
(events.map(testName) must contain("has a successful test", "has a failing test"))
36+
37+
private def otherSelectors =
38+
val events = runWithSelectors(List(new SuiteSelector, new TestSelector("hello")))
39+
(events must haveSize(3)) and
40+
(events.map(testName) must contain("has a successful test", "has a failing test", ".*"))
41+
42+
private def noMatches =
43+
val events = runWithSelectors(List(new TestSelector("won't match anything")))
44+
events must beEmpty
45+
46+
private def regexesAreEscaped =
47+
val events = runWithSelectors(new TestSelector(".*") :: Nil)
48+
(events must haveSize(1)) and
49+
(events.head.status() === Status.Success) and
50+
(testName(events.head) === ".*")
51+
52+
private def runWithSelectors(selectors: List[Selector]): List[Event] =
53+
val loggers = Array(NoLogger: Logger)
54+
val events = ArrayBuffer.empty[Event]
55+
val handler: EventHandler = (e: Event) => events.append(e)
56+
val framework = new Specs2Framework()
57+
val runner = framework.runner(Array.empty, Array.empty, getClass.getClassLoader)
58+
val fqcn = classOf[HelperSpec].getName
59+
60+
val taskDef = new TaskDef(fqcn, Fingerprints.fp1m, true, selectors.toArray)
61+
val tasks = runner.tasks(Array(taskDef))
62+
tasks.foreach(_.execute(handler, loggers))
63+
64+
events.toList
65+
66+
private def testName(event: Event): String =
67+
event.selector() match
68+
case ts: TestSelector => ts.testName()
69+
70+
private class HelperSpec extends Specification:
71+
def is = s2"""
72+
The helper spec
73+
has a successful test $ok
74+
has a failing test $ko
75+
.* $ok
76+
"""
77+
78+
private object NoLogger extends Logger:
79+
override def ansiCodesSupported(): Boolean = false
80+
override def error(msg: String): Unit = ()
81+
override def warn(msg: String): Unit = ()
82+
override def info(msg: String): Unit = ()
83+
override def debug(msg: String): Unit = ()
84+
override def trace(t: Throwable): Unit = ()

core/shared/src/main/scala/org/specs2/runner/SbtRunner.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ import org.specs2.fp.*, syntax.*
1616
import org.specs2.concurrent.ExecutionEnv
1717
import org.specs2.data.NamedTag
1818
import scala.util.*
19+
20+
import java.util.regex.Pattern
21+
1922
import scala.concurrent.duration.Duration
2023
import scala.concurrent.{Await, Future, ExecutionContext}
2124

@@ -38,7 +41,14 @@ abstract class BaseSbtRunner(args: Array[String], remoteArgs: Array[String], loa
3841

3942
/** create a new test task */
4043
def newTask(aTaskDef: TaskDef): Task =
41-
SbtTask(aTaskDef, env, loader, this)
44+
val fullEnv =
45+
if (env.arguments.select._ex.isDefined || aTaskDef.selectors().exists(!_.isInstanceOf[TestSelector])) env
46+
else
47+
val names = aTaskDef.selectors().toList.collect { case ts: TestSelector => Pattern.quote(ts.testName()) }
48+
val select = env.arguments.select.copy(_ex = Some(names.mkString("|")))
49+
env.setArguments(env.arguments.copy(select = select))
50+
51+
SbtTask(aTaskDef, fullEnv, loader, this)
4252

4353
def done =
4454
val result = env.shutdown()

0 commit comments

Comments
 (0)