diff --git a/.gitignore b/.gitignore index 6f1472c..0603afb 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,7 @@ target /\.manager/ /\.idea/ /\.idea_modules/ +.metals/ +.bloop/ +.bsp/ +.vscode/ diff --git a/src/main/java/com/novocode/junit/JUnitRunner.java b/src/main/java/com/novocode/junit/JUnitRunner.java index b00d803..6c773eb 100644 --- a/src/main/java/com/novocode/junit/JUnitRunner.java +++ b/src/main/java/com/novocode/junit/JUnitRunner.java @@ -3,6 +3,8 @@ import org.junit.runner.notification.RunListener; import sbt.testing.Runner; import sbt.testing.Task; +import sbt.testing.Selector; +import sbt.testing.TestSelector; import sbt.testing.TaskDef; import java.util.*; @@ -77,15 +79,38 @@ else if(s.startsWith("-D") && s.contains("=")) { @Override public Task[] tasks(TaskDef[] taskDefs) { used = true; - int length = taskDefs.length; - Task[] tasks = new Task[length]; - for (int i = 0; i < length; i++) { - TaskDef taskDef = taskDefs[i]; - tasks[i] = new JUnitTask(this, settings, taskDef); - } + Task[] tasks = Arrays + .stream(taskDefs) + .map(taskDef -> { + RunSettings alteredSettings = alterRunSettings(this.settings, taskDef.selectors()); + return new JUnitTask(this, alteredSettings, taskDef); + }) + .toArray(Task[]::new); return tasks; } + /** + * 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) { + boolean onlyTestSelectors = Arrays.stream(selectors).allMatch(selector -> selector instanceof TestSelector); + if (onlyTestSelectors) { + String testFilter = Arrays + .stream(selectors) + .map(selector -> ((TestSelector) selector).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; + } + private RunListener createRunListener(String runListenerClassName) { if(runListenerClassName != null) { try { diff --git a/src/main/java/com/novocode/junit/RunSettings.java b/src/main/java/com/novocode/junit/RunSettings.java index e8ac95d..c4c621d 100644 --- a/src/main/java/com/novocode/junit/RunSettings.java +++ b/src/main/java/com/novocode/junit/RunSettings.java @@ -50,6 +50,15 @@ class RunSettings { this.testFilter = testFilter; } + public RunSettings withTestFilter(String newTestFilter) { + String ignoreRunners = String.join(",", this.ignoreRunners); + return new RunSettings( + this.color, this.decodeScalaNames, this.quiet, this.verbosity, this.summary, this.logAssert, + ignoreRunners, this.logExceptionClass, this.sysprops, this.globPatterns, this.includeCategories, + this.excludeCategories, newTestFilter + ); + } + String decodeName(String name) { return decodeScalaNames ? decodeScalaName(name) : name; } @@ -108,7 +117,7 @@ String buildErrorMessage(Throwable t) { private String buildColoredName(Description desc, String c1, String c2, String c3) { StringBuilder b = new StringBuilder(); - + String cn = decodeName(desc.getClassName()); int pos1 = cn.indexOf('$'); int pos2 = pos1 == -1 ? cn.lastIndexOf('.') : cn.lastIndexOf('.', pos1); diff --git a/src/sbt-test/simple/check-test-selector/build.sbt b/src/sbt-test/simple/check-test-selector/build.sbt new file mode 100644 index 0000000..f4acffc --- /dev/null +++ b/src/sbt-test/simple/check-test-selector/build.sbt @@ -0,0 +1,6 @@ +name := "test-project" + +scalaVersion := "2.13.7" + +libraryDependencies += "com.github.sbt" % "junit-interface" % sys.props("plugin.version") % "test" +libraryDependencies += "org.scala-sbt" % "test-agent" % "1.5.5" % Test diff --git a/src/sbt-test/simple/check-test-selector/project/build.properties b/src/sbt-test/simple/check-test-selector/project/build.properties new file mode 100644 index 0000000..3161d21 --- /dev/null +++ b/src/sbt-test/simple/check-test-selector/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.6.1 diff --git a/src/sbt-test/simple/check-test-selector/src/test/scala/CheckTestSelector.scala b/src/sbt-test/simple/check-test-selector/src/test/scala/CheckTestSelector.scala new file mode 100644 index 0000000..3034f99 --- /dev/null +++ b/src/sbt-test/simple/check-test-selector/src/test/scala/CheckTestSelector.scala @@ -0,0 +1,102 @@ +import com.novocode.junit.JUnitFramework +import com.novocode.junit.JUnitFingerprint + +import org.junit.Test +import org.junit.Assert._ + +import sbt.testing._ +import scala.collection.mutable.ArrayBuffer + +/** + * Check if TestSelector's are correctly handled by JUnitRunner. + * Execute prepared TaskDef's using manually created instances of sbt.testing.{Framework and Runner}. + */ +class CheckTestSelector { + val framework = new JUnitFramework(); + val runner = framework.runner( + Array.empty[String], + Array.empty[String], + this.getClass().getClassLoader() + ); + + private def getEventHandler(): (ArrayBuffer[String], EventHandler) = { + val executedItems = new scala.collection.mutable.ArrayBuffer[String] + val eventHandler = new EventHandler { + override def handle(event: Event) = + if (event.status() == Status.Success) { + executedItems.addOne(event.fullyQualifiedName()) + } + } + (executedItems, eventHandler) + } + + private def getTaskDefs(selectors: Array[Selector]): Array[TaskDef] = { + Array( + new TaskDef("a.b.MyTestSuite", new JUnitFingerprint(), false, selectors) + ) + } + + @Test + def runAllViaSuiteSelector() { + val selectors = Array[Selector]( + new SuiteSelector + ) + val taskDefs = Array( + new TaskDef("a.b.MyTestSuite", new JUnitFingerprint(), false, selectors) + ) + + val tasks = runner.tasks(taskDefs) + assertEquals(tasks.size, 1) + val task = tasks(0) + + val (executedItems, eventHandler) = getEventHandler() + + task.execute(eventHandler, Nil.toArray) + assertArrayEquals( + Array[Object]("a.b.MyTestSuite.testBar", "a.b.MyTestSuite.testFoo"), + executedItems.toArray[Object] + ) + } + + @Test + def 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) + assertArrayEquals( + Array[Object]("a.b.MyTestSuite.testBar", "a.b.MyTestSuite.testFoo"), + executedItems.toArray[Object] + ) + } + + @Test + def 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) + assertArrayEquals( + Array[Object]("a.b.MyTestSuite.testFoo"), + executedItems.toArray[Object] + ) + + } +} diff --git a/src/sbt-test/simple/check-test-selector/src/test/scala/a/b/MyTestSuite.scala b/src/sbt-test/simple/check-test-selector/src/test/scala/a/b/MyTestSuite.scala new file mode 100644 index 0000000..4340a1a --- /dev/null +++ b/src/sbt-test/simple/check-test-selector/src/test/scala/a/b/MyTestSuite.scala @@ -0,0 +1,17 @@ +package a.b + +import org.junit.Test +import org.junit.Assert.assertEquals + +class MyTestSuite { + + @Test + def testFoo(): Unit = { + assertEquals("Test should pass", true, true) + } + + @Test + def testBar(): Unit = { + assertEquals("Test should pass", true, true) + } +} diff --git a/src/sbt-test/simple/check-test-selector/test b/src/sbt-test/simple/check-test-selector/test new file mode 100644 index 0000000..b2c98dd --- /dev/null +++ b/src/sbt-test/simple/check-test-selector/test @@ -0,0 +1,2 @@ +# make sure the unit test passes +> test