Skip to content

Commit 4baa72d

Browse files
committed
- when Selectors supplied include only TestSelectors and TestWildcardSelectors, filter properties to run by matching their names against the Selectors;
- match a property by both its short and full name; - added a test that actually runs ScalaCheck via the `SBT Test Interface` and demonstrates the (now correct) treatment of the `Selector`s; - test also demonstrates two unfixable infidelities in the treatment of the nested properties; - thankfully, ScalaCheck's implementation of `sbt.testing.Framework` is, unlike in some other test frameworks, shared between the platforms (JVM, Scala.js, Scala Native), so the fixes do not have to be replicated for each platform, but: - the test needs to supply a `testClassLoader: ClassLoader` parameter when calling `sbt.testing.Framework.runner()`; on platforms other than JVM, `getClass.getClassLoader` is not available, so `Platform.getClassLoader: ClassLoader` method was added to every `Platform`; on platforms other than the JVM, it returns `null`, which is fine since on those platforms `sbt.testing.Framework.runner()` ignores the `testClassLoader` parameter anyway. fixes #1105
1 parent c9aa8d3 commit 4baa72d

File tree

5 files changed

+90
-2
lines changed

5 files changed

+90
-2
lines changed

core/js/src/main/scala/org/scalacheck/Platform.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,6 @@ private[scalacheck] object Platform {
4646
}
4747

4848
type EnableReflectiveInstantiation = scala.scalajs.reflect.annotation.EnableReflectiveInstantiation
49+
50+
def getClassLoader: ClassLoader = null
4951
}

core/jvm/src/main/scala/org/scalacheck/Platform.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,6 @@ private[scalacheck] object Platform {
7373
Class.forName(name + "$", true, loader).getField("MODULE$").get(null)
7474

7575
class EnableReflectiveInstantiation extends scala.annotation.Annotation
76+
77+
def getClassLoader: ClassLoader = getClass.getClassLoader
7678
}

core/native/src/main/scala/org/scalacheck/Platform.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,6 @@ private[scalacheck] object Platform {
4646
}
4747

4848
type EnableReflectiveInstantiation = scala.scalanative.reflect.annotation.EnableReflectiveInstantiation
49+
50+
def getClassLoader: ClassLoader = null
4951
}

core/shared/src/main/scala/org/scalacheck/ScalaCheckFramework.scala

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,24 @@ private abstract class ScalaCheckRunner extends Runner {
9292
}
9393

9494
def rootTask(td: TaskDef): BaseTask = new BaseTask(td) {
95-
def execute(handler: EventHandler, loggers: Array[Logger]): Array[Task] =
96-
props.map(_._1).toSet.toArray map { name =>
95+
def execute(handler: EventHandler, loggers: Array[Logger]): Array[Task] = {
96+
val isTestsOnly: Boolean = td.selectors().forall(selector =>
97+
selector.isInstanceOf[TestSelector] ||
98+
selector.isInstanceOf[TestWildcardSelector]
99+
)
100+
101+
// Both the name used to define the property (`propName`)
102+
// and the name reported when it runs (`fullName`)
103+
// work when asking for a property by name or when using wildcards.
104+
105+
val prefix: String = properties.map(properties => s"${properties.name}.").getOrElse("")
106+
def isIncluded(name: String): Boolean = !isTestsOnly || td.selectors().exists {
107+
case s: TestSelector => name == s.testName() || name.stripPrefix(prefix) == s.testName()
108+
case s: TestWildcardSelector => name.contains(s.testWildcard())
109+
case _ => false
110+
}
111+
112+
props.map(_._1).toSet.filter(isIncluded).toArray map { name =>
97113
checkPropTask(
98114
new TaskDef(
99115
td.fullyQualifiedName(),
@@ -102,6 +118,7 @@ private abstract class ScalaCheckRunner extends Runner {
102118
Array(new TestSelector(name))),
103119
single = true)
104120
}
121+
}
105122
}
106123

107124
def checkPropTask(taskDef: TaskDef, single: Boolean): BaseTask = new BaseTask(taskDef) { self =>
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package org.scalacheck
2+
3+
import sbt.testing.{Event, EventHandler, Framework, Selector, SuiteSelector, Task, TaskDef, TestSelector, TestWildcardSelector}
4+
5+
object SbtFixture extends Properties("SbtFixture") {
6+
property("success") = Prop.passed
7+
}
8+
9+
object SbtNestingFixture extends Properties("SbtNestingFixture") {
10+
include(SbtFixture)
11+
}
12+
13+
object SbtSpecification extends Properties("Sbt") {
14+
property("suite") = run(Array(new SuiteSelector)) == List("SbtFixture.success")
15+
16+
property("exact" ) = run(Array(new TestSelector("success" ))) == List("SbtFixture.success")
17+
property("exactFull" ) = run(Array(new TestSelector("SbtFixture.success"))) == List("SbtFixture.success")
18+
property("exactMissing") = run(Array(new TestSelector("nonexistent"))) == List.empty
19+
20+
property("wildcard" ) = run(Array(new TestWildcardSelector("succ" ))) == List("SbtFixture.success")
21+
property("wildcardFull") = run(Array(new TestWildcardSelector("xture.succ"))) == List("SbtFixture.success")
22+
property("wildcardMissing") = run(Array(new TestWildcardSelector("prev"))) == List.empty
23+
24+
// Since ScalaCheck does not keep track what class/object a property belongs to,
25+
// the following two issues concerning nested properties can not be fixed:
26+
//
27+
// When `explicitlySpecified = true`, properties from objects other than the one being run
28+
// should *not* run (and the outcome should be `List.empty`) - but they do.
29+
//
30+
// When a property from an object other than the one being run *does* run,
31+
// its status should be reported with a `NestedTestSelector`
32+
// where `suiteId` names the object that the property belongs to -
33+
// but it is reported with a `TestSelector`.
34+
35+
property("nestedShouldRun") = run(
36+
Array(new SuiteSelector),
37+
fullyQualifiedName = "org.scalacheck.SbtNestingFixture",
38+
explicitlySpecified = false
39+
) == List("SbtNestingFixture.SbtFixture.success")
40+
41+
property("nestedShouldNotRun") = run(
42+
Array(new SuiteSelector),
43+
fullyQualifiedName = "org.scalacheck.SbtNestingFixture",
44+
explicitlySpecified = true
45+
) == List("SbtNestingFixture.SbtFixture.success")
46+
47+
// run using SBT Test Interface
48+
def run(
49+
selectors: Array[Selector],
50+
fullyQualifiedName: String = "org.scalacheck.SbtFixture",
51+
explicitlySpecified: Boolean = false
52+
): List[String] = {
53+
val framework: Framework = new ScalaCheckFramework
54+
var ran: List[String] = List.empty
55+
val eventHandler: EventHandler = (event: Event) => ran = ran.appended(event.selector().asInstanceOf[TestSelector].testName())
56+
def execute(tasks: Array[Task]): Unit = tasks.foreach(task => execute(task.execute(eventHandler, Array.empty)))
57+
execute(framework.runner(Array.empty, Array.empty, Platform.getClassLoader).tasks(Array(new TaskDef(
58+
fullyQualifiedName,
59+
framework.fingerprints()(2), // object ... extends org.scalacheck.Properties
60+
explicitlySpecified,
61+
selectors
62+
))))
63+
ran
64+
}
65+
}

0 commit comments

Comments
 (0)