From 4fae0431a608946917af329082a353fa5527dffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Podsiad=C5=82o?= <37124721+kpodsiad@users.noreply.github.com> Date: Mon, 21 Mar 2022 10:44:59 +0100 Subject: [PATCH] feat: take TestSelector into account (#501) More information can be found at https://github.com/sbt/junit-interface/pull/108 --- .../internal/junitinterface/JUnitRunner.java | 37 +++++- .../internal/junitinterface/RunSettings.java | 22 ++++ .../test/scala/munit/TestSelectorSuite.scala | 122 ++++++++++++++++++ 3 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 tests/jvm/src/test/scala/munit/TestSelectorSuite.scala diff --git a/junit-interface/src/main/java/munit/internal/junitinterface/JUnitRunner.java b/junit-interface/src/main/java/munit/internal/junitinterface/JUnitRunner.java index f21dcc83..6ce64576 100644 --- a/junit-interface/src/main/java/munit/internal/junitinterface/JUnitRunner.java +++ b/junit-interface/src/main/java/munit/internal/junitinterface/JUnitRunner.java @@ -11,8 +11,13 @@ import sbt.testing.Runner; import sbt.testing.Task; +import sbt.testing.Selector; +import sbt.testing.TestSelector; import sbt.testing.TaskDef; +import java.util.stream.Collectors; +import java.util.stream.Stream; + final class JUnitRunner implements Runner { private final String[] args; private final String[] remoteArgs; @@ -127,7 +132,8 @@ public Task[] tasks(TaskDef[] taskDefs) { for (int i = 0; i < length; i++) { TaskDef taskDef = taskDefs[i]; if (shouldRun(computer, taskDef)) { - tasks.add(new JUnitTask(this, settings, taskDef, computer)); + RunSettings alteredSettings = alterRunSettings(this.settings, taskDef.selectors()); + tasks.add(new JUnitTask(this, alteredSettings, taskDef, computer)); } } return tasks.toArray(new Task[0]); @@ -153,6 +159,35 @@ private RunListener createRunListener(String runListenerClassName) { } else return null; } + /** + * Alter default RunSettings depending on the passed selectors. If selectors contains only + * elements of type TestSelector, then default settings are altered to include only test names + * from these selectors. This allows to run particular test cases within given test class. + * testFilter is treated as a regular expression, hence joining is done via '|'. + */ + private RunSettings alterRunSettings(RunSettings defaultSettings, Selector[] selectors) { + List testSelectors = + Arrays.stream(selectors) + .flatMap( + selector -> { + return selector instanceof TestSelector + ? Stream.of((TestSelector) selector) + : Stream.empty(); + }) + .collect(Collectors.toList()); + if (testSelectors.size() == selectors.length) { + String testFilter = + testSelectors.stream().map(TestSelector::testName).collect(Collectors.joining("|")); + // if already provided testFilter is not empty add to it | (regex or operator) + String currentFilter = + defaultSettings.testFilter.length() > 0 ? defaultSettings.testFilter + "|" : ""; + String newFilter = currentFilter + testFilter; + return defaultSettings.withTestFilter(newFilter); + } + + return defaultSettings; + } + @Override public String done() { // Can't simply return the summary due to https://github.com/sbt/sbt/issues/3510 diff --git a/junit-interface/src/main/java/munit/internal/junitinterface/RunSettings.java b/junit-interface/src/main/java/munit/internal/junitinterface/RunSettings.java index a55f5eee..7b297753 100644 --- a/junit-interface/src/main/java/munit/internal/junitinterface/RunSettings.java +++ b/junit-interface/src/main/java/munit/internal/junitinterface/RunSettings.java @@ -78,6 +78,28 @@ class RunSettings implements Settings { this.trimStackTraces = trimStackTraces; } + public RunSettings withTestFilter(String newTestFilter) { + String ignoreRunners = String.join(",", this.ignoreRunners); + return new RunSettings( + this.color, + this.decodeScalaNames, + this.verbose, + this.useSbtLoggers, + this.useBufferedLoggers, + this.trimStackTraces, + this.summary, + this.logAssert, + ignoreRunners, + this.logExceptionClass, + this.sysprops, + this.globPatterns, + this.includeCategories, + this.excludeCategories, + this.includeTags, + this.excludeTags, + newTestFilter); + } + String decodeName(String name) { return decodeScalaNames ? decodeScalaName(name) : name; } diff --git a/tests/jvm/src/test/scala/munit/TestSelectorSuite.scala b/tests/jvm/src/test/scala/munit/TestSelectorSuite.scala new file mode 100644 index 00000000..79ac82f9 --- /dev/null +++ b/tests/jvm/src/test/scala/munit/TestSelectorSuite.scala @@ -0,0 +1,122 @@ +package munit + +import sbt.testing.Event +import sbt.testing.EventHandler +import sbt.testing.Selector +import sbt.testing.Status +import sbt.testing.SuiteSelector +import sbt.testing.TaskDef +import sbt.testing.TestSelector +import scala.collection.mutable +import sbt.testing.Runner + +/** + * Dummy test suite which is needed by TestSelectorSuite + */ +class MyTestSuite extends FunSuite { + + test("testFoo") { + assert(true, "Test should pass") + } + + test("testBar") { + assert(true, "Test should pass") + } +} + +/** + * Check if TestSelector's are correctly handled by Munit. + * Execute prepared TaskDef's using manually created instances of sbt.testing.{Framework and Runner}. + */ +class TestSelectorSuite extends FunSuite { + val framework = new Framework(); + val runner: Runner = framework.runner( + Array.empty, + Array.empty, + this.getClass().getClassLoader() + ); + + val fingerprint = framework.munitFingerprint + + /** + * Reference to the collection which will contain fully qualified names of executed tests. + * Only after finished execution it's safe to convert this mutable collection to immutable one. + */ + private def getEventHandler(): (mutable.ListBuffer[String], EventHandler) = { + val executedItems = new mutable.ListBuffer[String] + val eventHandler = new EventHandler { + override def handle(event: Event) = + if (event.status() == Status.Success) { + executedItems += event.fullyQualifiedName() + } + } + (executedItems, eventHandler) + } + + private def getTaskDefs(selectors: Array[Selector]): Array[TaskDef] = { + Array( + new TaskDef("munit.MyTestSuite", fingerprint, false, selectors) + ) + } + + test("runAllViaSuiteSelector") { + val selectors = Array[Selector]( + new SuiteSelector + ) + val taskDefs = Array( + new TaskDef("munit.MyTestSuite", fingerprint, false, selectors) + ) + + val tasks = runner.tasks(taskDefs) + assertEquals(tasks.size, 1) + val task = tasks(0) + + val (executedItems, eventHandler) = getEventHandler() + + task.execute(eventHandler, Nil.toArray) + assertEquals( + executedItems.toSet, + Set("munit.MyTestSuite.testBar", "munit.MyTestSuite.testFoo") + ) + } + + test("runAllViaTestSelectors") { + val selectors = Array[Selector]( + new TestSelector("testFoo"), + new TestSelector("testBar") + ) + val taskDefs = getTaskDefs(selectors) + + val tasks = runner.tasks(taskDefs) + assertEquals(tasks.size, 1) + val task = tasks(0) + + val (executedItems, eventHandler) = getEventHandler() + + task.execute(eventHandler, Nil.toArray) + assertEquals( + executedItems.toSet, + Set("munit.MyTestSuite.testBar", "munit.MyTestSuite.testFoo") + ) + } + + test("runOnlyOne") { + val selectors = Array[Selector]( + new TestSelector("testFoo") + ) + val taskDefs = getTaskDefs(selectors) + + val tasks = runner.tasks(taskDefs) + assertEquals(tasks.size, 1) + val task = tasks(0) + + val (executedItems, eventHandler) = getEventHandler() + + task.execute(eventHandler, Nil.toArray) + assertEquals( + executedItems.toSet, + Set("munit.MyTestSuite.testFoo") + ) + + } +}