Skip to content

Commit

Permalink
Allow for other usecases than Boolean => Unit
Browse files Browse the repository at this point in the history
This adds input/output type parameters to the Recorder :
* allows non boolean arguments to be captured
* allow non-unit types to be returned

The goal is to broaden the usage of this library, by letting people
extend the Recoder class to potentially capture other types / return
error-capable datatypes, etc
  • Loading branch information
Olivier Mélois authored and Baccata committed Jul 25, 2019
1 parent 3a70826 commit 8e1daf7
Show file tree
Hide file tree
Showing 7 changed files with 39 additions and 36 deletions.
8 changes: 4 additions & 4 deletions shared/src/main/scala-2/com/eed3si9n/expecty/Recorder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ package com.eed3si9n.expecty

import language.experimental.macros

abstract class Recorder {
def listener: RecorderListener[Boolean]
def apply(recording: Boolean): Unit = macro RecorderMacro1.apply
def apply(recording: Boolean, message: => String): Unit = macro RecorderMacro.apply
abstract class Recorder[R, A] {
def listener: RecorderListener[R, A]
def apply(recording: R): A = macro RecorderMacro1.apply[R, A]
def apply(recording: R, message: => String): A = macro RecorderMacro.apply[R, A]
}
21 changes: 11 additions & 10 deletions shared/src/main/scala-2/com/eed3si9n/expecty/RecorderMacro.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,23 @@ import scala.util.Properties
class RecorderMacro[C <: Context](val context: C) {
import context.universe._

def apply(recording: context.Tree, message: context.Tree): Expr[Unit] = {
context.Expr(Block(declareRuntime ::
def apply[R: context.WeakTypeTag, A: context.WeakTypeTag](recording: context.Tree, message: context.Tree): Expr[A] = {
context.Expr(Block(declareRuntime[R, A] ::
recordMessage(message) ::
recordExpressions(recording),
completeRecording))
}

private[this] def declareRuntime: Tree = {
val runtimeClass = context.mirror.staticClass(classOf[RecorderRuntime].getName)
private[this] def declareRuntime[R: context.WeakTypeTag, A : context.WeakTypeTag] : Tree = {
val runtimeClass = context.mirror.staticClass(classOf[RecorderRuntime[_, _]].getName())
ValDef(
Modifiers(),
termName(context)("$com_eed3si9n_expecty_recorderRuntime"),
TypeTree(runtimeClass.toType),
TypeTree(weakTypeOf[RecorderRuntime[R, A]]),
Apply(
Select(
New(Ident(runtimeClass)),
termName(context)("<init>")),
termNames.CONSTRUCTOR),
List(
Select(
context.prefix.tree,
Expand Down Expand Up @@ -67,6 +67,7 @@ class RecorderMacro[C <: Context](val context: C) {
termName(context)("completeRecording")),
List())


private[this] def resetValues: Tree =
Apply(
Select(
Expand Down Expand Up @@ -142,13 +143,13 @@ Instrumented AST: ${showRaw(instrumented)}")
}

object RecorderMacro1 {
def apply(context: Context)(recording: context.Tree): context.Expr[Unit] = {
new RecorderMacro[context.type](context).apply(recording, context.literal("").tree)
def apply[R: context.WeakTypeTag, A: context.WeakTypeTag](context: Context)(recording: context.Tree): context.Expr[A] = {
new RecorderMacro[context.type](context).apply[R, A](recording, context.literal("").tree)
}
}

object RecorderMacro {
def apply(context: Context)(recording: context.Tree, message: context.Tree): context.Expr[Unit] = {
new RecorderMacro[context.type](context).apply(recording, message)
def apply[R: context.WeakTypeTag, A : context.WeakTypeTag](context: Context)(recording: context.Tree, message: context.Tree): context.Expr[A] = {
new RecorderMacro[context.type](context).apply[R, A](recording, message)
}
}
8 changes: 4 additions & 4 deletions shared/src/main/scala-3/com/eed3si9n/expecty/Recorder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ package com.eed3si9n.expecty

import language.experimental.macros

abstract class Recorder {
def listener: RecorderListener[Boolean]
inline def apply(recording: Boolean): Unit =
abstract class Recorder[R, A] {
def listener: RecorderListener[R, A]
inline def apply(recording: R): A =
${ RecorderMacro.apply('recording, 'listener) }
inline def apply(recording: Boolean, message: => String): Unit =
inline def apply(recording: R, message: => String): A =
${ RecorderMacro.apply('recording, 'message, 'listener) }
}
19 changes: 9 additions & 10 deletions shared/src/main/scala-3/com/eed3si9n/expecty/RecorderMacro.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ import scala.tasty._
object RecorderMacro {
implicit val toolbox: scala.quoted.Toolbox = scala.quoted.Toolbox.make(getClass.getClassLoader)

def apply(
recording: Expr[Boolean],
listener: Expr[RecorderListener[Boolean]]) given (qctx: QuoteContext): Expr[Unit] = {
def apply[R: Type, A: Type](
recording: Expr[R],
listener: Expr[RecorderListener[R, A]]) given (qctx: QuoteContext): Expr[A] = {
apply(recording, '{""}, listener)
}

def apply(
recording: Expr[Boolean],
def apply[R: Type, A: Type](
recording: Expr[R],
message: Expr[String],
listener: Expr[RecorderListener[Boolean]]) given (qctx: QuoteContext): Expr[Unit] = {
listener: Expr[RecorderListener[R, A]]) given (qctx: QuoteContext): Expr[A] = {
import qctx.tasty._
val termArg: Term = recording.unseal.underlyingArgument

Expand All @@ -38,10 +38,10 @@ object RecorderMacro {
}

'{
val recorderRuntime: RecorderRuntime = new RecorderRuntime($listener)
val recorderRuntime: RecorderRuntime[R, A] = new RecorderRuntime($listener)
recorderRuntime.recordMessage($message)
${
val runtimeSym = '[RecorderRuntime].unseal.symbol match {
val runtimeSym = '[RecorderRuntime[_, _]].unseal.symbol match {
case IsClassDefSymbol(sym) => sym
}
val recordExpressionSel: Term = {
Expand Down Expand Up @@ -151,9 +151,8 @@ object RecorderMacro {
Block(
recordExpressions(termArg),
'{ recorderRuntime.completeRecording() }.unseal
).seal
).seal.cast[A]
}
()
}
}
}
7 changes: 5 additions & 2 deletions shared/src/main/scala/com/eed3si9n/expecty/Expecty.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@

package com.eed3si9n.expecty

class Expecty extends Recorder {
class Expecty extends Recorder[Boolean, Unit] {
val failEarly: Boolean = true
val showTypes: Boolean = false
// val printAsts: Boolean = false
// val printExprs: Boolean = false

class ExpectyListener extends RecorderListener[Boolean] {
class ExpectyListener extends RecorderListener[Boolean, Unit] {
override def expressionRecorded(
recordedExpr: RecordedExpression[Boolean], recordedMessage: Function0[String]): Unit = {
lazy val rendering: String = new ExpressionRenderer(showTypes).render(recordedExpr)
Expand All @@ -35,6 +35,9 @@ class Expecty extends Recorder {
throw new AssertionError(header + "\n\n" + rendering)
}
}

override def recordingCompleted(
recording: Recording[Boolean], recordedMessage: Function0[String]) = {}
}

override lazy val listener = new ExpectyListener
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
*/
package com.eed3si9n.expecty

trait RecorderListener[T] {
trait RecorderListener[T, A] {
def valueRecorded(recordedValue: RecordedValue): Unit = {}
def expressionRecorded(recordedExpr: RecordedExpression[T], recordedMessage: Function0[String]): Unit = {}
def recordingCompleted(recording: Recording[T], recordedMessage: Function0[String]): Unit = {}
def recordingCompleted(recording: Recording[T], recordedMessage: Function0[String]): A
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
package com.eed3si9n.expecty

// one instance per recording
class RecorderRuntime(listener: RecorderListener[Boolean]) {
class RecorderRuntime[R, A](listener: RecorderListener[R, A]) {
protected var recordedValues: List[RecordedValue] = _
protected var recordedExprs: List[RecordedExpression[Boolean]] = List.empty
protected var recordedExprs: List[RecordedExpression[R]] = List.empty
protected var recordedMessage: Function0[String] = () => ""

def resetValues(): Unit = {
Expand All @@ -34,13 +34,13 @@ class RecorderRuntime(listener: RecorderListener[Boolean]) {
recordedMessage = () => message
}

def recordExpression(text: String, ast: String, value: Boolean): Unit = {
def recordExpression(text: String, ast: String, value: R): Unit = {
val recordedExpr = RecordedExpression(text, ast, value, recordedValues)
listener.expressionRecorded(recordedExpr, recordedMessage)
recordedExprs = recordedExpr :: recordedExprs
}

def completeRecording(): Unit = {
def completeRecording(): A = {
val lastRecorded = recordedExprs.head
val recording = Recording(lastRecorded.value, recordedExprs)
listener.recordingCompleted(recording, recordedMessage)
Expand Down

0 comments on commit 8e1daf7

Please sign in to comment.