ScalaCheck spec seeking Matcher[String] after upgrade - scala

After upgrading from specs 2.4.13 to 3.7.1
"foo" should {
"bar" >> prop((i: Int) =>
i % 50 must be>= 0
)
}
no longer compiles. It fails with
type mismatch;
[error] found : org.specs2.specification.core.Fragment
[error] required: org.specs2.matcher.Matcher[String]
[error] "bar" >> prop((i: Int) =>
[error] ^
Changing it to
"foo" >> {
"bar" >> prop((i: Int) =>
i % 50 must be>= 0
)
}
will allow it to compile and pass.
Has the behaviour of should been changed?

I don't get such error in 3.8.8. However this might be the result of should being used for a block of examples and should as a matcher construct (a should be_>=(0)).
You can remove the latter usage by mixing-in org.specs2.matcher.NoShouldExpectations

Related

Can Specs2 Result.foreach be made to work like a matcher?

Using Specs2, if I do:
1 must beEqualTo(2)
1 must beEqualTo(1)
The test fails (as expected)
If I do:
Result.foreach(1 to 10) { i =>
i must_== 2
}
Result.foreach(1 to 10) { i =>
i must_== i
}
The test passes
For Result.foreach I have to use and to make the test fail (as expected):
Result.foreach(1 to 10) { i =>
i must_== 2
} and
Result.foreach(1 to 10) { i =>
i must_== i
}
Why is this?
Is there a way to make it work in a less surprising way? This is very error prone - it's way too easy to not notice somebody didn't type and
I checked the Specs2 user guide but there's no mention of this behaviour that I can find.
This should probably be better documented. The Result.foreach function is indeed supposed to be non side-effecting. If you want side-effects you can call foreach directly in your specification because this one is wired in with the "throwing" behaviour of a mutable specification:
class TestMutableSpec extends mutable.Specification {
"foreach must fail" >> {
foreach(1 to 10) { i =>
i must_== 2
}
foreach(1 to 10) { i =>
i must_== i
}
}
}
returns
[info] TestMutableSpec
[error] x foreach must fail
[error] There are 9 failures
[error] 1 != 2
[error] 3 != 2
[error] 4 != 2
[error] 5 != 2
[error] 6 != 2
[error] 7 != 2
[error] 8 != 2
[error] 9 != 2
[error] 10 != 2 (TestSpec.scala:19)
[info] Total for specification TestMutableSpec
[info] Finished in 121 ms
[info] 1 example, 1 failure, 0 error
Workaround: mix AllExpectations into your Specification:
class QuickStartSpec extends mutable.Specification with AllExpectations {
"foreach must fail" >> {
Result.foreach(1 to 10) { i =>
i must_== 2
}
Result.foreach(1 to 10) { i =>
i must_== i
}
}
}
This produces:
sbt> testOnly QuickStartSpec
[info] QuickStartSpec
[error] x foreach must fail
[error] 1 != 2 (QuickStartSpec.scala:8)
Note: I'm aware of the fact that this is not the primary purpose of the AllExpectations trait. However, this seems a sensible solution, given that Result.foreach behaves in a different way than a matcher inside a mutable specification.
Also, according to the docs, the goal of "unit specifications" is to let users have multiple expectations per example without the need to use and. In light of this, the behavior of Result.foreach indeed seems misleading. If you think this is a bug, you may consider opening an issue.

How to get source directories of all project dependencies of an sbt project?

I'm trying to implement an sbt task which collects all the source directories of projects specified using dependsOn method of a project. I end up with this piece of code:
def sourcesOfDependencies(p: Project): Def.Initialize[Task[Seq[File]]] =
Def.taskDyn {
p.dependencies
.map(dep => (sourceDirectory in dep.project).toTask.map(Seq(_)))
.reduce((t1, t2) =>
t1.flatMap(s1 => t2.map(s2 => s1 ++ s2).taskValue)
)
}
sources := Def.taskDyn { sourcesOfDependencies(myProject) }.value
As for me it should work but it fails to compile:
[error] /home/visa/src/Playtech-BIT/project/SparkDeployment.scala:57:32: The evaluation of `map` inside an anonymous function is prohibited.
[error]
[error] Problem: Task invocations inside anonymous functions are evaluated independently of whether the anonymous function is invoked or not.
[error]
[error] Solution:
[error] 1. Make `map` evaluation explicit outside of the function body if you don't care about its evaluation.
[error] 2. Use a dynamic task to evaluate `map` and pass that value as a parameter to an anonymous function.
[error]
[error] t1.flatMap(s1 => t2.map(s2 => s1 ++ s2).taskValue)
[error] ^
[error] /home/visa/src/Playtech-BIT/project/SparkDeployment.scala:57:26: Illegal dynamic reference: t2
[error] t1.flatMap(s1 => t2.map(s2 => s1 ++ s2).taskValue)
[error] ^
[error] /home/visa/src/Playtech-BIT/project/SparkDeployment.scala:57:39: Illegal dynamic reference: s1
[error] t1.flatMap(s1 => t2.map(s2 => s1 ++ s2).taskValue)
Could anyone advise on how to apply the proposed solution? Or maybe there is a simpler way to achieve my goal?
I've been struggling with a similar problem but finally found an answer.
So, what you may do is to define a dynamic task performing your complex computation, like
val buildMaping: Def.Initialize[Task[Seq[Def.Initialize[(ProjectRef, Seq[Seq[File]])]]]] = {
Def.taskDyn {
val refs = loadedBuild.value.allProjectRefs
val tt = refs.map(_._1).map {
ref =>
sourceDirectories.all(ScopeFilter(inProjects(ref)))
.zipWith(Def.setting(ref)) { case (a, b) => b -> a }
}
Def.task {
tt
}
}
}
So, this buildMapping allows you to get a map of project references to their source directories.
then invoke your task this way:
Def.task {
val sd = settingsData.in(Global).value
val mapping = buildMaping.value.map(_.evaluate(sd)).toMap
val allProjectRefs = loadedBuild.value.allProjectRefs.map(_._1)
//... now you may iterate over each project,
// get deps and use the mapping to resolve your values
}

Scala quasiquote macro example broken - type signatures off

I took this Scala quasiquote example from the book "Programming Scala" (2nd Edition)
I am getting this error: https://issues.scala-lang.org/browse/SI-9711
The type inference says "Trees#Tree", but the type inference is off.
import scala.reflect.api.Trees // For Trees#Tree (TreeNode)
import scala.reflect.macros.blackbox._
import scala.reflect.runtime.universe._ // To use Scala runtime reflection
/**
* Represents a macro invariant which is checked over the corresponding statements.
* Example:
* '''
* var mustBeHello = "Hello"
* invariant.execute(mustBeHello.equals("Hello")) {
* mustBeHello = "Goodbye"
* }
* // Throws invariant.InvariantFailure
* '''
*/
object invariant {
case class InvariantFailure(message: String) extends RuntimeException(message)
type SyntaxTree = scala.reflect.runtime.universe.Tree
type TreeNode = Trees#Tree // a syntax tree node that is in and of itself a tree
// These two methods are the same, but one is a function call and the other is a macro function call
def execute[RetType] (myPredicate: => Boolean)(block: => RetType): RetType = macro executeMacro
def executeMacro(context: Context)(myPredicate: SyntaxTree)(block: SyntaxTree) = {
val predicateString: String = showCode(myPredicate) // turn this predicate into a String
val q"..$statements" = block // make the block into a sequence of statements
val myStatements: Seq[TreeNode] = statements // the statements are a sequence of SyntaxTreeNodes, each node a little Tree
val invariantStatements = statements.flatMap { statement =>
// Error here:
val statementString: String = showCode(statement) /* Type mismatch, expected Tree, actual Trees#Tree */
val message: String =
s"FAILURE! $predicateString == false, for statement: " + statementString
val tif: SyntaxTree =
q"throw new metaprogramming.invariant.InvariantFailure($message)"
val predicate2: SyntaxTree =
q"if (false == $myPredicate) $tif"
val toReturn: List[SyntaxTree] =
List(q"{ val temp = $myStatements; $predicate2; temp };")
toReturn
}
val tif: SyntaxTree =
q"throw new metaprogramming.invariant.InvariantFailure($predicateString)"
val predicate: SyntaxTree =
q"if (false == $predicate) $tif"
val toReturn: SyntaxTree =
q"$predicate; ..$invariantStatements"
toReturn
}
}
^ The documentation should be self explanatory. The type inference says Tree#Tree, but adding ":Tree#Tree" to the example code kills compilation with error:
[info] Compiling 2 Scala sources to /home/johnreed/sbtProjects/scala-trace-debug/target/scala-2.11/test-classes...
[error] /home/johnreed/sbtProjects/scala-trace-debug/src/test/scala/mataprogramming/invariant2.scala:30: type mismatch;
[error] found : TreeNode
error scala.reflect.api.Trees#Tree
[error] required: context.universe.Tree
[error] val exceptionMessage = s"FAILURE! $predicateAsString == false, for statement: " + showCode(statement)
I'm getting "Type mismatch, expected Tree, actual Trees#Tree" in IntelliJ
[info] Compiling 2 Scala sources to /home/johnreed/sbtProjects/scala-trace-debug/target/scala-2.11/test-classes...
[error] /home/johnreed/sbtProjects/scala-trace-debug/src/test/scala/mataprogramming/invariant2.scala:30: type mismatch;
[error] found : TreeNode
error scala.reflect.api.Trees#Tree
[error] required: context.universe.Tree
[error] val exceptionMessage = s"FAILURE! $predicateAsString == false, for statement: " + showCode(statement)
There's something really funky with the types. Either IntelliJ is messing up the types or the notion escapes me.
The "normal" way to discover an inferred type is one of:
ask the REPL using :type
assign to a bad type and observe the error message
call a function that displays a TypeTag of the thing
For example,
[error] /home/apm/clones/prog-scala-2nd-ed-code-examples/src/main/scala/progscala2/metaprogramming/invariant2.scala:25: type mismatch;
[error] found : List[context.universe.Tree]
[error] required: Int
[error] val foo: Int = statements
[error] ^
This shows that the Tree is path-dependent in the context universe.
You can't feed it just any old tree.
Similar issue at this question.

Menu.param 2 params, not compile

I'm around trying to understand how "Menu.param" works with 2 params
I'm using this code as an example:
https://github.com/dpp/starting_point/blob/menu_fun/src/main/scala/code/snippet/AThread.scala
But I can not make it work
object APost {
// Create a menu for /user/santo
val menu = Menu.param[( User, Posts )]("ParamId1", "ParamId2",
{
case User(p1) :: Posts(p2) :: Nil =>
Full( (p1, p2) )
case _ =>
Empty
},
params => List(params._1.id.toString,params._2.id.toString)) / * / * >> LocGroup("UserPost")
lazy val loc = menu.toLoc
def render = "*" #> loc.currentValue.map(_.docId)
}
When compiling sends me the following error:
[error] /menu2params/src/main/scala/code/snippet/APost.scala:23: constructor cannot be instantiated to expected type;
[error] found : scala.collection.immutable.::[B]
[error] required: String
[error] case ParamId1(p1) :: ParamId1(p2) :: Nil =>
[error] ^
[error] one error found
[error] (compile:compile) Compilation failed
Someone who can help me please
Here is the copy of my project:
https://github.com/hectorgool/menu2params/blob/master/src/main/scala/code/snippet/APost.scala
Thanks for your attention
Menu.param is for use with a single parameter, hence the error requiring a String instead of a List. Menu.params allows you to specify multiple parameters in a list. Making that change to your code should resolve the issue.

How do I verify invokations with specific string matchers in Specs2 with Mockito

I have a test along these lines:
httpClient.post(anyString, anyString) returns (first, second)
//do my thing
there were two(httpClient).post(anyString, anyString)
This works fine, but I want to verify that the first call passes a different body than the second call. The body is rather big and I don't want to do precise matching on a strict example. I've tried this:
there was one(httpClientMock).postMessage(anyString, argThat(contain("FOO"))
there was one(httpClientMock).postMessage(anyString, argThat(contain("FOO"))
That makes Mockito complain:
InvalidUseOfMatchersException:
[error] Invalid use of argument matchers!
[error] 2 matchers expected, 3 recorded:
I've also tried:
there was one(httpClientMock).postMessage(argThat(contain("foo")), argThat(contain("FOO")))
there was one(httpClientMock).postMessage(argThat(contain("foo")), argThat(contain("FOO")))
which results in:
Wanted 1 time:
[error] -> ...
[error] But was 2 times. Undesired invocation: ...
It seems to me that something like this should be possible, but I can't seem to figure it out. Insights?
I think that this is more of a problem with Mockito to begin with. When you're using Mockito with specs2 and you're in doubt, always drop down to the direct Mockito API:
// simplified httpClient with only one parameter
val httpClient = mock[HttpClient]
httpClient.post(anyString) returns ""
httpClient.post("s1")
httpClient.post("s2")
// forget specs2
// there was two(httpClient).post(anyString)
org.mockito.Mockito.verify(httpClient, org.mockito.Mockito.times(1)).post("s1")
// I guess that you don't want this to pass but it does
org.mockito.Mockito.verify(httpClient, org.mockito.Mockito.times(1)).post("s1")
One possible way around this is to define a matcher that will check the successive values of an argument:
there was two(httpClient).post(consecutiveValues(===("s1"), ===("s2")))
And the consecutiveValues matcher is defined as such:
import matcher._
import MatcherImplicits._
// return a matcher that will check a value against a different
// `expected` matcher each time it is invoked
def consecutiveValues[T](expected: Matcher[T]*): Matcher[T] = {
// count the number of tested values
var i = -1
// store the results
var results: Seq[(Int, MatchResult[T])] = Seq()
def result(t: T) = {
i += 1
// with Mockito values are tested twice
// the first time we return the result (but also store it)
// the second time we return the computed result
if (i < expected.size) {
val mr = expected(i).apply(Expectable(t))
results = results :+ (i, mr)
mr
} else results(i - expected.size)._2
}
// return a function that is translated to a specs2 matcher
// thanks to implicits
// display the failing messages if there are any
(t: T) => (result(t).isSuccess,
results.filterNot(_._2.isSuccess).map { case (n, mr) =>
s"value $n is incorrect: ${mr.message}" }.mkString(", "))
}
You can test the code above. The failure messages are not the best but do the trick. In this situation:
httpClient.post("s1")
httpClient.post("s2")
there was two(httpClient).post(consecutiveValues(===("s1"), ===("s3")))
You will see:
[error] x test
[error] The mock was not called as expected:
[error] httpClient.post(
[error] value 1 is incorrect: 's2' is not equal to 's3'
[error] );
[error] Wanted 2 times:
[error] -> at ...
[error] But was 1 time:
[error] -> at ...