forked from mockito/mockito-scala
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathVerifyMacro.scala
More file actions
129 lines (105 loc) · 5.2 KB
/
Copy pathVerifyMacro.scala
File metadata and controls
129 lines (105 loc) · 5.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package org.mockito
import org.mockito.Utils.*
import org.mockito.internal.MacroDebug.debugResult
import scala.reflect.macros.blackbox
object Called {
def by[T](stubbing: T): T = macro DoSomethingMacro.calledBy[T]
}
object VerifyMacro extends VerificationMacroTransformer {
// Re-export runtime objects so existing code referencing VerifyMacro.* continues to work
val Never = VerifyMacroRuntime.Never
val NeverAgain = VerifyMacroRuntime.NeverAgain
val Once = VerifyMacroRuntime.Once
def wasMacro[T: c.WeakTypeTag, R](c: blackbox.Context)(called: c.Tree)(order: c.Expr[VerifyOrder]): c.Expr[R] = {
val r = c.Expr[R](transformVerification(c)(c.macroApplication))
debugResult(c)("mockito-print-verify")(r.tree)
r
}
def wasNeverCalledAgainMacro[T: c.WeakTypeTag, R](c: blackbox.Context)(called: c.Tree)($ev: c.Tree): c.Expr[R] = {
val r = c.Expr[R](transformVerification(c)(c.macroApplication))
debugResult(c)("mockito-print-verify")(r.tree)
r
}
}
private[mockito] trait VerificationMacroTransformer {
protected def transformInvocation(c: blackbox.Context)(invocation: c.Tree, order: c.Tree, times: c.Tree): c.Tree = {
import c.universe.*
try doTransformInvocation(c)(invocation, order, times)
catch {
case e: Exception => throw new Exception(s"Error when transforming invocation ${show(invocation)}", e)
}
}
protected def doTransformInvocation(c: blackbox.Context)(invocation: c.Tree, order: c.Tree, times: c.Tree): c.Tree = {
import c.universe.*
val pf: PartialFunction[c.Tree, c.Tree] = {
case q"$obj.$method[..$targs](...$args)" =>
val newArgs = args.map(a => transformArgs(c)(a))
q"verification($order.verifyWithMode($obj, $times).$method[..$targs](...$newArgs))"
case q"$obj.$method[..$targs]" =>
q"verification($order.verifyWithMode($obj, $times).$method[..$targs])"
}
if (pf.isDefinedAt(invocation))
pf(invocation)
else if (invocation.children.nonEmpty && pf.isDefinedAt(invocation.children.last)) {
val vals = invocation.children.dropRight(1)
val valsByName = vals.collect { case line @ q"$_ val $name:$_ = $value" =>
name.toString -> (value.asInstanceOf[c.Tree], line)
}.toMap
val inlinedArgsCall = invocation.children.last match {
case q"$obj.$method[..$targs](...$args)" =>
val newArgs = args.map { a =>
transformArgs(c)(a).map {
case p if show(p).startsWith("x$") => transformArg(c)(valsByName(p.toString)._1)
case other => other
}
}
q"verification($order.verifyWithMode($obj, $times).$method[..$targs](...$newArgs))"
}
val call = show(inlinedArgsCall)
val usedVals = valsByName.collect {
case (name, (_, line)) if call.contains(name) => line
}
q"..$usedVals; $inlinedArgsCall"
} else throw new Exception(s"Couldn't recognize invocation ${show(invocation)}")
}
protected def transformVerification[T: c.WeakTypeTag, R](c: blackbox.Context)(called: c.Tree): c.Tree = {
import c.universe.*
def transformMockWasNeverCalled(obj: c.Tree, called: c.Tree): c.Tree =
called match {
case q"$_.called" => q"verification(_root_.org.mockito.MockitoSugar.verifyZeroInteractions($obj))"
case q"$_.calledAgain" => q"verification(_root_.org.mockito.MockitoSugar.verifyNoMoreInteractions($obj))"
case q"$_.calledAgain.apply($_.ignoringStubs)" =>
q"verification(_root_.org.mockito.MockitoSugar.verifyNoMoreInteractions(_root_.org.mockito.MockitoSugar.ignoreStubs($obj).toIndexedSeq: _*))"
}
called match {
case q"$_.VerifyingOps[$_]($invocation).was($_.called)($order)" =>
transformInvocation(c)(invocation, order, q"_root_.org.mockito.VerifyMacroRuntime.Once")
case q"$_.VerifyingOps[$_]($a.$b).wasNever($called)($order)" =>
q"""
if (_root_.org.mockito.MockitoSugar.mockingDetails($a).isMock) {
${called match {
case q"$_.calledAgain" =>
val calledPattern = show(q"$a.$b")
q"""throw new _root_.org.mockito.exceptions.misusing.NotAMockException(Seq(
"[" + $calledPattern + "] is not a mock!",
"Example of correct verification:",
" myMock wasNever called",
""
).mkString("\n"))
"""
case _ => transformInvocation(c)(q"$a.$b", order, q"_root_.org.mockito.VerifyMacroRuntime.Never")
}}
} else {
${transformMockWasNeverCalled(q"$a.$b", called)}
}
"""
case q"$_.VerifyingOps[$_]($obj.$method[..$targs](...$args)).wasNever($_.called)($order)" =>
transformInvocation(c)(q"$obj.$method[..$targs](...$args)", order, q"_root_.org.mockito.VerifyMacroRuntime.Never")
case q"$_.VerifyingOps[$_]($obj).wasNever($called)($_)" =>
transformMockWasNeverCalled(obj, called)
case q"$_.VerifyingOps[$_]($invocation).wasCalled($times)($order)" =>
transformInvocation(c)(invocation, order, times)
case o => throw new Exception(s"VerifyMacro: Couldn't recognize ${show(o)}")
}
}
}