Skip to content

Commit

Permalink
Remove recursive search for clue function.
Browse files Browse the repository at this point in the history
  • Loading branch information
zainab-ali committed Sep 17, 2024
1 parent 396247b commit c942b9e
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 42 deletions.
79 changes: 37 additions & 42 deletions modules/core/shared/src/main/scala-2/weaver/ExpectMacro.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,73 +12,71 @@ private[weaver] trait ExpectMacro {
private[weaver] object ExpectMacro {
def allImpl(c: blackbox.Context)(values: c.Tree*): c.Tree = {
import c.universe._
val loc = new weaver.macros.Macros(c).fromContext.asInstanceOf[c.Tree]
val sourceLoc = new weaver.macros.Macros(c).fromContext.asInstanceOf[c.Tree]
val (cluesName, cluesValDef) = makeClues(c)
val clueMethodSymbol = getClueMethodSymbol(c)

val outputTrees =
values.toList.map(transform(c)(clueMethodSymbol, cluesName, _))
makeBlock(c)(cluesName, cluesValDef, outputTrees, loc, q"None")
val transformedValues =
values.toList.map(replaceClueMethodCalls(c)(clueMethodSymbol,
cluesName,
_))
makeExpectations(c)(cluesName,
cluesValDef,
transformedValues,
sourceLoc,
q"None")
}
def messageImpl(c: blackbox.Context)(
value: c.Tree,
message: c.Tree): c.Tree = {
import c.universe._
val loc = new weaver.macros.Macros(c).fromContext.asInstanceOf[c.Tree]
val sourceLoc = new weaver.macros.Macros(c).fromContext.asInstanceOf[c.Tree]
val (cluesName, cluesValDef) = makeClues(c)
val clueMethodSymbol = getClueMethodSymbol(c)

val outputTree =
transform(c)(clueMethodSymbol, cluesName, value)
makeBlock(c)(cluesName,
cluesValDef,
List(outputTree),
loc,
q"Some($message)")
val transformedValue =
replaceClueMethodCalls(c)(clueMethodSymbol, cluesName, value)
makeExpectations(c)(cluesName,
cluesValDef,
List(transformedValue),
sourceLoc,
q"Some($message)")
}

def applyImpl(c: blackbox.Context)(value: c.Tree): c.Tree = {

import c.universe._
val loc = new weaver.macros.Macros(c).fromContext.asInstanceOf[c.Tree]
val sourceLoc = new weaver.macros.Macros(c).fromContext.asInstanceOf[c.Tree]
val (cluesName, cluesValDef) = makeClues(c)
val clueMethodSymbol = getClueMethodSymbol(c)

val outputTree =
transform(c)(clueMethodSymbol, cluesName, value)
makeBlock(c)(cluesName, cluesValDef, List(outputTree), loc, q"None")
val transformedValue =
replaceClueMethodCalls(c)(clueMethodSymbol, cluesName, value)
makeExpectations(c)(cluesName,
cluesValDef,
List(transformedValue),
sourceLoc,
q"None")
}

private def makeBlock(c: blackbox.Context)(
private def makeExpectations(c: blackbox.Context)(
cluesName: c.TermName,
cluesValDef: c.Tree,
values: List[c.Tree],
loc: c.Tree,
sourceLoc: c.Tree,
message: c.Tree): c.Tree = {
import c.universe._
val block =
q"$cluesValDef; _root_.weaver.internals.Clues.toExpectations($loc, $message, $cluesName, ..$values)"
q"$cluesValDef; _root_.weaver.internals.Clues.toExpectations($sourceLoc, $message, $cluesName, ..$values)"
val untyped = c.untypecheck(block)
val retyped = c.typecheck(untyped, pt = c.typeOf[Expectations])
retyped

}

// TODO: Zainab - This approach will not work for `import Expectations.Helpers.clue`.
private def getClueMethodSymbol(c: blackbox.Context): c.Symbol = {
import c.universe._
val clueFunctionsBaseClass = symbolOf[ClueHelpers]
def go(s: c.Symbol, depth: Int): c.Symbol =
if (s.isClass && s.asClass.baseClasses.contains(clueFunctionsBaseClass)) {
s.info.member(TermName("clue"))
} else {
if (depth < 100) {
go(s.owner, depth + 1)
} else throw new Error(
s"Failed to find module! ${c.internal.enclosingOwner}")
}
val parent = go(c.internal.enclosingOwner, 0)
parent
symbolOf[ClueHelpers].info.member(TermName("clue"))
}

private def makeClues(c: blackbox.Context): (c.TermName, c.Tree) = {
Expand All @@ -89,27 +87,24 @@ private[weaver] object ExpectMacro {
(cluesName, cluesValDef)
}

private def transform(c: blackbox.Context)(
private def replaceClueMethodCalls(c: blackbox.Context)(
clueMethodSymbol: c.Symbol,
cluesName: c.TermName,
value: c.Tree): c.Tree = {

import c.universe._
object mytransformer extends Transformer {
object transformer extends Transformer {

override def transform(input: Tree): Tree = input match {
case c.universe.Apply(fun, List(clueSource))
case c.universe.Apply(fun, List(clueValue))
if fun.symbol == clueMethodSymbol =>
val transformedValue = super.transform(clueSource)
val clueValueName = TermName(c.freshName("clueValue$"))
val clueValueTree = q"$clueValueName"
val transformedTree =
q"""{val $clueValueName = ${transformedValue}; ${cluesName}.addClue($clueValueTree)}"""
transformedTree
val transformedClueValue = super.transform(clueValue)
val clueName = TermName(c.freshName("clue$"))
q"""{val $clueName = ${transformedClueValue}; ${cluesName}.addClue($clueName)}"""
case o => super.transform(o)
}
}

mytransformer.transform(value)
transformer.transform(value)
}
}
23 changes: 23 additions & 0 deletions modules/framework-cats/shared/src/test/scala/DogFoodTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,29 @@ object DogFoodTests extends IOSuite {
expect.same(actual, expected)
}
}
test("clue calls are replaced when using helper objects") {
_.runSuite(Meta.Clue).map {
case (logs, _) =>
val actual = extractLogEventAfterFailures(logs) {
case LoggedEvent.Error(msg) if msg.contains("(helpers)") =>
msg
}.get

val expected = s"""
|- (helpers) 0ms
| assertion failed (modules/framework-cats/shared/src/test/scala/Meta.scala:115)
|
| Clues {
| z: Int = 3
| y: Int = 2
| x: Int = 1
| }
|
""".stripMargin.trim

expect.same(actual, expected)
}
}

private def outputBeforeFailures(logs: Chain[LoggedEvent]): Chain[String] = {
logs
Expand Down
10 changes: 10 additions & 0 deletions modules/framework-cats/shared/src/test/scala/Meta.scala
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,16 @@ object Meta {

expect(clue(x) == clue(y))
}

pureTest("(helpers)") {
val x = 1
val y = 2
val z = 3
import Expectations.Helpers.{ clue => otherclue }
object CustomHelpers extends Expectations.Helpers
expect(CustomHelpers.clue(x) == otherclue(y) || x == clue(z))
}

}

object FailingTestStatusReporting extends SimpleIOSuite {
Expand Down

0 comments on commit c942b9e

Please sign in to comment.