Wrap argThat in a named function - scala

Say I have something like this (VERY OVER-SIMPLIFIED):
case class Foo(bar: String)
val mockFnThatTakesFoo = mock[Foo => Unit]
def fooHasBarSetTo(expectedBar: String, foo: Foo): Boolean = {
val actualBar = foo.bar
actualBar shouldEqual expectedBar
true
}
mockFnThatTakesFoo(argThat(fooHasBarSetTo("blah", _))) wasCalled once
This works. However, the assertion itself is a little bit convoluted and it could be made more readable.
I tried this:
val withFooHavingBarSetTo = (expectedBar: String) => argThat(fooHasBarSetTo(expectedBar, _))
//and then
mockFnThatTakesFoo(withFooHavingBarSetTo("blah")) wasCalled once
Much neater! but doesn't work :/
> [info] FooSpec:
[info] - should do stuff *** FAILED ***
[info] org.mockito.exceptions.misusing.InvalidUseOfMatchersException: Invalid use of argument matchers!
[info] 1 matchers expected, 2 recorded:
[info] -> at com.foo.FooSpec.argThat(FooSpec.scala:28)
[info] -> at com.foo.FooSpec.$anonfun$new$5(FooSpec.scala:204)
Any idea how can this be done?

I believe Mockito is implemented using macro and it tracks positions where argThat and other Mockito methods are placed. For example if you try to create a variable like
val blahBar = argThat(fooHasBarSetTo("blah", _))
You will get an exception "Misplaced or misused argument matcher detected here". So it is impossible to return argThat matcher from another method.
If you need to reuse mock matcher with different argument value I see only one possible solution like
def mockCalledOnceWithFooHavingBar(expectedBar: String) {
mockFnThatTakesFoo(argThat(fooHasBarSetTo(expectedBar, _))) wasCalled once
}
mockCalledOnceWithFooHavingBar("blah")
mockCalledOnceWithFooHavingBar("bar")

Related

Scala 3 Macros: How to invoke a method obtained as a `Symbol` in a quoted code block?

In a Scala 3 macro that takes a type parameter T, you can use TypeRepr.of[T] and the new Scala 3 reflection API to explore the companionClass of T, and find the Symbol for an arbitrary method on that companion class (eg companionClass.declarations.find(_.name == "list") to find a list() method).
Given the Symbol for a companion object method, how would you then invoke that method within a quoted code block?
I'm guessing I would need to convert that Symbol to a Expr[T], but I don't know how to do that!
In a Scala 2 macro, the invocation of a listMethod of type c.universe.Symbol in a q"..." quasiquote seems pretty simple - just say $listMethod, and then you can start mapping on the resulting list, eg:
q"""
$listMethod.map(_.toString)
"""
Trying to do a similar thing in a Scala 3 macro gets an error like this:
[error] 27 | ${listMethod}.map(_.toString)
[error] | ^^^^^^^^^^
[error] | Found: (listMethod : x$1.reflect.Symbol)
[error] | Required: quoted.Expr[Any]
What is the correct code to get this working in Scala 3?
You can see more code context in the AvroSerialisableMacro classes (Scala 2 compiles, Scala 3 currently nowhere near!) here: https://github.com/guardian/marley/pull/77/files
First, let's talk how to call a method using symbol name in general.
You might need Select. You can call obtain it in a a few different ways, e.g.:
New(TypeTree.of[YourType]).select(primaryConstructor) // when you want to create something
expression.asTerm.select(method) // when you want to call it on something
Once you selected method you can provide arguments:
select.appliedToArgs(args) // if there is only 1 argument list
select.appliedToArgss(args) // if there is more than one argument list
// (type parameter list is listed in paramSymss
// but shouldn't be used here, so filter it out!)
select.appliedToNone // if this is a method like "def method(): T"
// (single, but empty, parameter list)
select.appliedToArgss(Nil) // is this is a method like "def method: T"
// (with not even empty parameter list)
There are also other methods like appliedToType, appliedToTypeTrees, but if you have a method name as a Symbol and want to use it to call something this should be a good starting point.
And remember that source code of Quotes is your friend, so even when your IDE doesn't give you any suggestions, it can point you towards some solution.
In theory these methods are defined on Term rather than Select (<: Term) but your use case will be most likely picking an expression and calling a method on it with some parameters. So a full example could be e.g.
val expression: Expr[Input]
val method: Symbol
val args: List[Term]
// (input: Input).method(args) : Output
expression // Expr[Input]
.asTerm // Term
.select(method) // Select
.appliedToArgs(args) // Term
.asExpr // Expr[?]
.asExprOf[Output] // Expr[Output]
Obviously, proving that the expression can call method and making sure that types of Terms in args match allowed types of values that you pass to the method, is on you. It is a bit more hassle than it was in Scala 2 since quotes allow you to work with Type[T] and Expr[T] only, so anything that doesn't fall under that category has to be implemented with macros/Tasty ADT until you get to the point that you can return Expr inside ${}.
That said, the example you linked shows that these calls are rather hardcoded, so you don't have to look up Symbols and call them. Your code will most likely do away with:
// T <: ThriftEnum
// Creating companion's Expr can be done with asExprOf called on
// Ref from Dmytro Mitin's answer
def findCompanionOfThisOrParent(): Expr[ThriftEnumObject[T]] = ...
// _Expr_ with the List value you constructed instead of Symbol!
val listOfValues: Expr[List[T]] = '{
${ findCompanionOfThisOrParent() }.list
}
// once you have an Expr you don't have to do any magic
// to call a method on it, Quotes works nice
'{
...
val valueMap = Map(${ listOfValues }.map(x => x ->
org.apache.avro.generic.GenericData.get.createEnum(
com.gu.marley.enumsymbols.SnakesOnACamel.toSnake(x.name), schemaInstance)
): _*)
...
}
Difference between Scala 2 quasiquotes and Scala 3 quotations is that the former must compile during compile time of the main code using macros (i.e. during macro expansion, macro runtime) while the latter must compile earlier, at macro compile time. So Scala 3 quotations '{...}/${...} are more like Scala 2 reify{...}/.splice than Scala 2 quasiquotes q"..."/${...}.
`tq` equivalent in Scala 3 macros
You have to re-create AST. Let's see what shape AST should have:
object B:
def fff(): Unit = ()
import scala.quoted.*
inline def foo(): Unit = ${fooImpl}
def fooImpl(using Quotes): Expr[Unit] =
import quotes.reflect.*
println('{B.fff()}.asTerm.show(using Printer.TreeStructure))
'{()}
foo() // ... Apply(Select(Ident("B"), "fff"), Nil)
So in order to re-create AST try to use Apply(...) and Select.unique(..., "list"):
import scala.quoted.*
inline def foo[T](): Unit = ${fooImpl[T]}
def fooImpl[T: Type](using Quotes): Expr[Unit] =
import quotes.reflect.*
val sym = TypeRepr.of[T].typeSymbol
'{
println("aaa")
${
Apply(
Select.unique(
Ref(sym.companionModule),
"list"
),
Nil
).asExprOf[Unit]
}
}
Testing (in a different file):
class A
object A {
def list(): Unit = println("list")
}
foo[A]()
//scalac: {
// scala.Predef.println("aaa")
// A.list()
//}
// prints at runtime:
// aaa
// list
Using method symbol rather than its name and using convenience methods rather than AST nodes directly, you can rewrite fooImpl as
def fooImpl[T: Type](using Quotes): Expr[Unit] =
import quotes.reflect.*
val sym = TypeRepr.of[T].typeSymbol
val listMethod = sym.companionClass.declarations.find(_.name == "list").get
'{
println("aaa")
${
Ref(sym.companionModule)
.select(listMethod)
.appliedToArgs(Nil)
.asExprOf[Unit]
}
}
This is just an example how to create an AST. You should use your actual return type of def list() instead of Unit in .asExprOf[Unit].
How to get the list of default fields values for typed case class?
scala 3 macro how to implement generic trait

Exclude a specific implicit from a Scala project

How can I prevent the usage of a specific implicit in my scala code?
For example, I was recently bit by the default Codec provided by https://github.com/scala/scala/blob/68bad81726d15d03a843dc476d52cbbaf52fb168/src/library/scala/io/Codec.scala#L76.
Is there a way to ensure that any code that calls for an implicit codec: Codec never uses the one provided by fallbackSystemCodec?
Alternatively, is it possible to block all implicit Codecs?
Is this something that should be doable using scalafix?
Scalafix can inspect implicit arguments using SemanticTree. Here is an example solution by defining a custom scalafix rule.
Given
import scala.io.Codec
object Hello {
def foo(implicit codec: Codec) = 3
foo
}
we can define a custom rule
class ExcludedImplicitsRule(config: ExcludedImplicitsRuleConfig)
extends SemanticRule("ExcludedImplicitsRule") {
...
override def fix(implicit doc: SemanticDocument): Patch = {
doc.tree.collect {
case term: Term if term.synthetic.isDefined => // TODO: Use ApplyTree(func, args)
val struct = term.synthetic.structure
val isImplicit = struct.contains("implicit")
val excludedImplicit = config.blacklist.find(struct.contains)
if (isImplicit && excludedImplicit.isDefined)
Patch.lint(ExcludedImplicitsDiagnostic(term, excludedImplicit.getOrElse(config.blacklist.mkString(","))))
else
Patch.empty
}.asPatch
}
}
and corresponding .scalafix.conf
rule = ExcludedImplicitsRule
ExcludedImplicitsRuleConfig.blacklist = [
fallbackSystemCodec
]
should enable sbt scalafix to raise the diagnostic
[error] /Users/mario/IdeaProjects/scalafix-exclude-implicits/example-project/scalafix-exclude-implicits-example/src/main/scala/example/Hello.scala:7:3: error: [ExcludedImplicitsRule] Attempting to pass excluded implicit fallbackSystemCodec to foo'
[error] foo
[error] ^^^
[error] (Compile / scalafix) scalafix.sbt.ScalafixFailed: LinterError
Note the output of println(term.synthetic.structure)
Some(ApplyTree(
OriginalTree(Term.Name("foo")),
List(
IdTree(SymbolInformation(scala/io/LowPriorityCodecImplicits#fallbackSystemCodec. => implicit lazy val method fallbackSystemCodec: Codec))
)
))
Clearly the above solution is not efficient as it searches strings, however it should give some direction. Perhaps matching on ApplyTree(func, args) would be better.
scalafix-exclude-implicits-example shows how to configure the project to use ExcludedImplicitsRule.
You can do this by using a new type altogether; this way, nobody will be able to override it in your dependencies. It's essentially the answer I posted to create an ambiguous low priority implicit
It may not be practical though, if for example you can't change the type.

Mocking a function with pass-by-name arguments

My actual use-case is unit testing code involving finagle FuturePool: I want to make sure, FuturePool.apply was actually invoked, so that the task was executed in the correct instance of the pool.
The problem I am running into however seems more generic, so I will illustrate it on an abstract example, not related to finagle or futures.
Suppose, I have these two classes:
class Foo {
def apply(f: => String) = f
}
class Bar(val foo: Foo) {
def doit(f: => String) = foo(f)
}
Bar has an instance of Foo, that knows how to run functions, and I want to test that it is actually using it for execution:
describe("Bar") {
it("should use the right foo") {
val foo = mock[Foo]
when(foo.apply(any)).thenAnswer( new Answer[String] {
def answer(invocation: InvocationOnMock): String =
invocation.getArgumentAt(0, classOf[Function0[String]]).apply()
})
new Bar(foo).doit("foo") should equal("foo")
}
}
This does not work: .doit return null, apparently, because mockito does not realize it was mocked. It seems that any is not matching Function0 in this case (replacing it with any[Function0[String]] does not help either.
I also tried it another way:
it("should Foo!") {
val foo = Mockito.spy(new Foo)
new Bar(foo).doit("foo") should equal("foo")
verify(foo).apply(any)
}
This also does not work, and kinda confirms my suspicion about any not working in this case:
Argument(s) are different! Wanted:
foo$1.apply(
($anonfun$apply$mcV$sp$7) <function0>
);
Actual invocation has different arguments:
foo$1.apply(
($anonfun$apply$mcV$sp$6) <function0>
);
Any ideas about a good way to get around this?
This signature:
def apply(f: => String)
is known as "call by name" where it passes an expression instead of an evaluated expression. This is very specific to Scala and not well supported with Mockito.
There is a host of workarounds to this:
Is there a way to match on a call-by-name argument of a Mockito mock object in Specs?
How to mock a method with functional arguments in Scala?
How do you mock scala call-by name in Mockito
The one by Eric looks the simplest and what you may want.

overloaded method value with alternatives: when calling Call.absoluteURL with request

Here is my code
class AuthAction(callbackUri:String) extends ActionBuilder[UserRequest] with ActionRefiner[Request,UserRequest]{
override def refine[A](request: Request[A]): Future[Either[Result,UserRequest[A]]] = {
val redirectUri = routes.Application.callback(None, None).absoluteURL(request)
getUser(request) match {
case Some(user) => Future.successful(Right(new UserRequest(user,request)))
case _ => Future.successful(Left(oauthLogin(callbackUri,redirectUri)(request)))
}
}
}
When i try to compile this code, i get the following error
[error] (secure: Boolean)(implicit request: play.api.mvc.RequestHeader)String <and>
[error] (x$1: play.mvc.Http.Request)String
[error] cannot be applied to (play.api.mvc.Request[A])
[error] val redirectUri = routes.Application.callback(None, None).absoluteURL(request)
is this something to do with implicit params? whats happening here?
It wants play.mvc.Http.Request, and you are trying to pass in play.api.mvc.Request. They are not compatible.
Edit: To answer the question you are asking in the comment ...
Well, I am not sure which parameter you are talking about. There are two flavors of this function:
The one, that it seems like you are trying to call -
absoluteURL(request: play.mvc.Http.Request) - does not take any implicit params, it just needs the request of the right type.
The other one - absoluteURL(secure: Boolean)(implicit request: play.api.mvc.RequestHeader) - does have an implicit parameter, which is also of a different type from what you have (RequestHeader, not Request).
If you declare a variable holding this requestHeader as implicit, and it is in scope, you can call the latter function without specifying it explicitly:
implicit val requestHeader = createMyRequestHeader()
routes.Application.callback(None, None).absoluteURL(true)
or you can still pass it in explicitly like you would do with a regular parameter (in this case you don't need it to be declared as implicit):
routes.Application.callback(None, None).absoluteURL(true)(requestHeader)

Enumeration and mapping with Scala 2.10

I'm trying to port my application to Scala 2.10.0-M2. I'm seeing some nice improvements with better warnings from compiler. But I also got bunch of errors, all related to me mapping from Enumeration.values.
I'll give you a simple example. I'd like to have an enumeration and then pre-create bunch of objects and build a map that uses enumeration values as keys and then some matching objects as values. For example:
object Phrase extends Enumeration {
type Phrase = Value
val PHRASE1 = Value("My phrase 1")
val PHRASE2 = Value("My phrase 2")
}
class Entity(text:String)
object Test {
val myMapWithPhrases = Phrase.values.map(p => (p -> new Entity(p.toString))).toMap
}
Now this used to work just fine on Scala 2.8 and 2.9. But 2.10.0-M2 gives me following warning:
[ERROR] common/Test.scala:21: error: diverging implicit expansion for type scala.collection.generic.CanBuildFrom[common.Phrase.ValueSet,(common.Phrase.Value, common.Entity),That]
[INFO] starting with method newCanBuildFrom in object SortedSet
[INFO] val myMapWithPhrases = Phrase.values.map(p => (p -> new Entity(p.toString))).toMap
^
What's causing this and how do you fix it?
It's basically a type mismatch error. You can work around it by first converting is to a list:
scala> Phrase.values.toList.map(p => (p, new Entity(p.toString))).toMap
res15: scala.collection.immutable.Map[Phrase.Value,Entity] = Map(My phrase 1 -> Entity#d0e999, My phrase 2 -> Entity#1987acd)
For more information, see the answers to What's a “diverging implicit expansion” scalac message mean? and What is a diverging implicit expansion error?
As you can see from your error, the ValueSet that holds the enums became a SortedSet at some point. It wants to produce a SortedSet on map, but can't sort on your Entity.
Something like this works with case class Entity:
implicit object orderingOfEntity extends Ordering[Entity] {
def compare(e1: Entity, e2: Entity) = e1.text compare e2.text
}