Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run tests according to selectors #316

Merged
merged 1 commit into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 36 additions & 8 deletions utest/src/utest/runner/BaseRunner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package runner
//import acyclic.file
import sbt.testing._

import scala.concurrent.Future
import scala.util.Failure
import utest.framework.{StackMarker, Tree}
object BaseRunner{
Expand Down Expand Up @@ -73,14 +74,35 @@ abstract class BaseRunner(val args: Array[String],
throw new NoSuchTestException(unknownPaths:_*)
}else for{
taskDef <- taskDefs
if BaseRunner.checkOverlap(query, taskDef.fullyQualifiedName().split('.'))
} yield makeTask(taskDef)
fromSelectors = pathsFromSelectors(taskDef)
fullQuery = TestQueryParser.collapse(query ++ fromSelectors)
if BaseRunner.checkOverlap(fullQuery, taskDef.fullyQualifiedName().split('.'))
} yield makeTask(taskDef, fullQuery)
}

private def pathsFromSelectors(taskDef: TaskDef): TestQueryParser#Trees = {
if (taskDef.selectors().exists(!_.isInstanceOf[NestedTestSelector])) Nil
else {
val testNames = taskDef.selectors().collect {
case nts: NestedTestSelector => s"${nts.suiteId()}.${nts.testName()}"
}
try TestQueryParser(testNames.mkString(","))
catch { case _: Throwable => Nil }
}
}

@deprecated("Use the variant that takes the full query instead")
def runSuite(loggers: Seq[Logger],
suiteName: String,
eventHandler: EventHandler,
taskDef: TaskDef): Future[Unit] =
runSuite(loggers, suiteName, eventHandler, taskDef, query)

def runSuite(loggers: Seq[Logger],
suiteName: String,
eventHandler: EventHandler,
taskDef: TaskDef) = {
taskDef: TaskDef,
fullQuery: TestQueryParser#Trees): Future[Unit] = {

startHeader.foreach(h => println(h(path.fold("")(" " + _))))

Expand All @@ -99,6 +121,8 @@ abstract class BaseRunner(val args: Array[String],
}
def fingerprint() = taskDef.fingerprint()
def duration() = millis

override def toString: String = selector().toString
})
}
}
Expand Down Expand Up @@ -140,7 +164,7 @@ abstract class BaseRunner(val args: Array[String],
}

}
rec(query, suiteName.split('.').toList)
rec(fullQuery, suiteName.split('.').toList)
}

implicit val ec = utest.framework.ExecutionContext.RunNow
Expand Down Expand Up @@ -180,12 +204,16 @@ abstract class BaseRunner(val args: Array[String],
}


private def makeTask(taskDef: TaskDef): sbt.testing.Task = {
new runner.Task(taskDef, runSuite(_, taskDef.fullyQualifiedName(), _, taskDef))
private def makeTask(taskDef: TaskDef, fullQuery: TestQueryParser#Trees): sbt.testing.Task = {
new runner.Task(taskDef, runSuite(_, taskDef.fullyQualifiedName(), _, taskDef, fullQuery))
}
// Scala.js test interface specific methods
def deserializeTask(task: String, deserializer: String => TaskDef): sbt.testing.Task =
makeTask(deserializer(task))
def deserializeTask(task: String, deserializer: String => TaskDef): sbt.testing.Task = {
val taskDef = deserializer(task)
val fromSelectors = pathsFromSelectors(taskDef)
val fullQuery = TestQueryParser.collapse(query ++ fromSelectors)
makeTask(taskDef, fullQuery)
}

def serializeTask(task: sbt.testing.Task, serializer: TaskDef => String): String =
serializer(task.taskDef())
Expand Down
76 changes: 76 additions & 0 deletions utest/test/src-jvm/test/utest/SelectorTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package test.utest

import sbt.testing._
import utest._
import utest.runner.{Fingerprint, Framework}

import scala.collection.mutable.ArrayBuffer

object SelectorTest extends utest.TestSuite {
private val helperFqcn = HelperTest.getClass.getName.stripSuffix("$")

def tests = Tests {
test("SuiteSelector runs entire suite") {
val events = runWithSelectors(new SuiteSelector :: Nil)
val expectedNames = Set(s"$helperFqcn.simple", s"$helperFqcn.successful test", s"$helperFqcn.with a nested test.this is nested")
assert(events.map(testName).toSet == expectedNames)
}

test("NestedSuiteSelector runs only the matching test") {
val events = runWithSelectors(new NestedTestSelector(helperFqcn, "simple") :: Nil)
assert(events.map(testName) == List(s"$helperFqcn.simple"))
}

test("NestedSuiteSelector works with spaces") {
val events = runWithSelectors(new NestedTestSelector(helperFqcn, "successful test") :: Nil)
assert(events.map(testName) == List(s"$helperFqcn.successful test"))
}

test("NestedSuiteSelector works with nested tests") {
val events = runWithSelectors(new NestedTestSelector(helperFqcn, "with a nested test") :: Nil)
val expected = List(s"$helperFqcn.with a nested test.this is nested")
assert(events.map(testName) == expected)

val events2 = runWithSelectors(new NestedTestSelector(helperFqcn, "with a nested test.this is nested") :: Nil)
assert(events2.map(testName) == expected)
}
}

private def testName(event: Event): String = event.selector() match {
case nts: NestedTestSelector => s"${nts.suiteId()}.${nts.testName()}"
}

private def runWithSelectors(selectors: List[Selector]): List[Event] = {
val loggers = Array(NoLogger: Logger)
val events = ArrayBuffer.empty[Event]
val handler: EventHandler = (e: Event) => events.append(e)
val framework = new Framework()
val runner = framework.runner(Array.empty, Array.empty, getClass.getClassLoader)

val taskDef = new TaskDef(helperFqcn, Fingerprint, true, selectors.toArray)
val tasks = runner.tasks(Array(taskDef))
tasks.foreach(_.execute(handler, loggers))

events.toList
}

}

private object HelperTest extends utest.TestSuite {
def tests = Tests {
test("simple") { () }
test("successful test") { () }
test("with a nested test") {
test("this is nested") { () }
}
}
}

private object NoLogger extends Logger {
override def ansiCodesSupported(): Boolean = false
override def error(msg: String): Unit = ()
override def warn(msg: String): Unit = ()
override def info(msg: String): Unit = ()
override def debug(msg: String): Unit = ()
override def trace(t: Throwable): Unit = ()
}