ex1 and ex2 done
This commit is contained in:
parent
fa3972ec1b
commit
16b4367ed0
4 changed files with 22 additions and 20 deletions
|
@ -8,6 +8,11 @@ import scala.language.postfixOps
|
||||||
*/
|
*/
|
||||||
object GenerativeFluentAssertionsDSL:
|
object GenerativeFluentAssertionsDSL:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Due to operator precedence, '!==' is the only comparison operator that is considered an "assignment operation"
|
||||||
|
* and thus has lower precedence than alphanumeric operators. Therefore, we must handle "!==" as the final link of
|
||||||
|
* a method chain instead of a predicate builder on `satisfying`, like the other operators.
|
||||||
|
*/
|
||||||
extension [T](inline property: SatisfyingNotEquals[T])
|
extension [T](inline property: SatisfyingNotEquals[T])
|
||||||
inline def `!==`(inline obj: T)(using inline ord: Ordering[T]): AssertionProperty =
|
inline def `!==`(inline obj: T)(using inline ord: Ordering[T]): AssertionProperty =
|
||||||
${ notEqualsImpl('property, 'obj, 'ord) }
|
${ notEqualsImpl('property, 'obj, 'ord) }
|
||||||
|
@ -91,7 +96,7 @@ object GenerativeFluentAssertionsDSL:
|
||||||
// and the type of the property (actually... not exactly that. Why?)
|
// and the type of the property (actually... not exactly that. Why?)
|
||||||
case '{
|
case '{
|
||||||
(${ typeProvider }: HavePropertyTypeProvider[subjectType])
|
(${ typeProvider }: HavePropertyTypeProvider[subjectType])
|
||||||
.applyDynamic($propertyNameExpr: String)($valueExpr: valueType => Boolean)
|
.applyDynamic($propertyNameExpr: String)($valueExpr: Condition[valueType])
|
||||||
.$asInstanceOf$[AssertionProperty]
|
.$asInstanceOf$[AssertionProperty]
|
||||||
} =>
|
} =>
|
||||||
println(assertionExpr.show)
|
println(assertionExpr.show)
|
||||||
|
@ -162,7 +167,7 @@ object GenerativeFluentAssertionsDSL:
|
||||||
private def generateShouldHaveAssertion[T, R](
|
private def generateShouldHaveAssertion[T, R](
|
||||||
subjectExpr: Expr[T],
|
subjectExpr: Expr[T],
|
||||||
propertyNameExpr: Expr[String],
|
propertyNameExpr: Expr[String],
|
||||||
propertyValueExpr: Expr[R => Boolean]
|
propertyValueExpr: Expr[Condition[R]]
|
||||||
)(using Type[T], Type[R])(using Quotes): Expr[Unit] =
|
)(using Type[T], Type[R])(using Quotes): Expr[Unit] =
|
||||||
import quotes.reflect.*
|
import quotes.reflect.*
|
||||||
|
|
||||||
|
@ -195,11 +200,11 @@ object GenerativeFluentAssertionsDSL:
|
||||||
*/
|
*/
|
||||||
val assertion = '{
|
val assertion = '{
|
||||||
val subject = $subjectExpr
|
val subject = $subjectExpr
|
||||||
val tester = $propertyValueExpr
|
val condition = $propertyValueExpr
|
||||||
lazy val value = ${ Select(('subject).asTerm, candidateMethod).asExprOf[R] }
|
lazy val value = ${ Select(('subject).asTerm, candidateMethod).asExprOf[R] }
|
||||||
assert(
|
assert(
|
||||||
tester.apply(value),
|
condition.test(value),
|
||||||
s"assertion failed for ${subject}.${$propertyNameExpr}"
|
s"${subject}.${$propertyNameExpr} " + condition.failedMessage + " but " + value
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
report.info(assertion.show, subjectExpr.asTerm.pos)
|
report.info(assertion.show, subjectExpr.asTerm.pos)
|
||||||
|
|
|
@ -19,19 +19,17 @@ end DynamicShouldBeProperty
|
||||||
*/
|
*/
|
||||||
sealed trait SatisfyingNotEquals[T]
|
sealed trait SatisfyingNotEquals[T]
|
||||||
|
|
||||||
|
case class Condition[T](predicate: (T, T) => Boolean, op: String, expected: T):
|
||||||
|
def test(value: T): Boolean = predicate(value, expected)
|
||||||
|
def failedMessage: String = s"is not ${op} ${expected}"
|
||||||
|
|
||||||
case object satisfying:
|
case object satisfying:
|
||||||
def `===`[T](toWhat: T)(using ord: Ordering[T]): T => Boolean =
|
def `===`[T](toWhat: T)(using ord: Ordering[T]): Condition[T] = Condition(ord.equiv, "===", toWhat)
|
||||||
something => ord.equiv(something, toWhat)
|
def notEquals[T](toWhat: T, ord: Ordering[T]): Condition[T] = Condition(!ord.equiv(_, _), "!==", toWhat)
|
||||||
def notEquals[T](toWhat: T, ord: Ordering[T]): T => Boolean =
|
def `<`[T](toWhat: T)(using ord: Ordering[T]): Condition[T] = Condition(ord.lt, "<", toWhat)
|
||||||
something => !ord.equiv(something, toWhat)
|
def `>`[T](toWhat: T)(using ord: Ordering[T]): Condition[T] = Condition(ord.gt, ">", toWhat)
|
||||||
def `<`[T](toWhat: T)(using ord: Ordering[T]): T => Boolean =
|
def `<=`[T](toWhat: T)(using ord: Ordering[T]): Condition[T] = Condition(ord.lteq, "<=", toWhat)
|
||||||
something => ord.lt(something, toWhat)
|
def `>=`[T](toWhat: T)(using ord: Ordering[T]): Condition[T] = Condition(ord.gteq, ">=", toWhat)
|
||||||
def `>`[T](toWhat: T)(using ord: Ordering[T]): T => Boolean =
|
|
||||||
something => ord.gt(something, toWhat)
|
|
||||||
def `<=`[T](toWhat: T)(using ord: Ordering[T]): T => Boolean =
|
|
||||||
something => ord.lteq(something, toWhat)
|
|
||||||
def `>=`[T](toWhat: T)(using ord: Ordering[T]): T => Boolean =
|
|
||||||
something => ord.gteq(something, toWhat)
|
|
||||||
|
|
||||||
class BePropertyTypeProvider[T](val subject: T) extends Selectable:
|
class BePropertyTypeProvider[T](val subject: T) extends Selectable:
|
||||||
def selectDynamic(fieldName: String): AssertionProperty = ???
|
def selectDynamic(fieldName: String): AssertionProperty = ???
|
||||||
|
|
|
@ -45,7 +45,7 @@ case class Box(label: String, weight: Mass, price: Money)
|
||||||
List() should have size satisfying >= 0
|
List() should have size satisfying >= 0
|
||||||
List(3) should have size satisfying !== 0
|
List(3) should have size satisfying !== 0
|
||||||
List(50, 2, 3) should have head satisfying < 100
|
List(50, 2, 3) should have head satisfying < 100
|
||||||
box should have weight satisfying >= 3.0.kg
|
box should have weight satisfying <= 300.0.kg
|
||||||
|
|
||||||
/* should have assertions */
|
/* should have assertions */
|
||||||
// // assert(List(1).head == 1, ...)
|
// // assert(List(1).head == 1, ...)
|
||||||
|
|
|
@ -32,9 +32,8 @@ object ShouldHaveTypeProvider:
|
||||||
// The type of the field, or the return type of the method.
|
// The type of the field, or the return type of the method.
|
||||||
val fieldTypeRepr = subjectTypeRepr.memberType(symbol).widen
|
val fieldTypeRepr = subjectTypeRepr.memberType(symbol).widen
|
||||||
|
|
||||||
val appliedType = AppliedType(typeConstructor[? => ?], List(fieldTypeRepr, TypeRepr.of[Boolean]))
|
|
||||||
val methodType = MethodType(List("arg"))(
|
val methodType = MethodType(List("arg"))(
|
||||||
_ => List(appliedType),
|
_ => List(AppliedType(typeConstructor[Condition[?]], List(fieldTypeRepr))),
|
||||||
_ => TypeRepr.of[AssertionProperty]
|
_ => TypeRepr.of[AssertionProperty]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue