ex2 works

This commit is contained in:
Claudio Maggioni 2024-12-18 14:01:08 +01:00
parent dda2d1b61f
commit fa3972ec1b
3 changed files with 26 additions and 12 deletions

View file

@ -8,6 +8,25 @@ import scala.language.postfixOps
*/ */
object GenerativeFluentAssertionsDSL: object GenerativeFluentAssertionsDSL:
extension [T](inline property: SatisfyingNotEquals[T])
inline def `!==`(inline obj: T)(using inline ord: Ordering[T]): AssertionProperty =
${ notEqualsImpl('property, 'obj, 'ord) }
private def notEqualsImpl[T: Type](property: Expr[SatisfyingNotEquals[T]], obj: Expr[T], ord: Expr[Ordering[T]])(
using Quotes
): Expr[AssertionProperty] = {
import quotes.reflect.*
property match
case '{ (${ typeProvider }: HavePropertyTypeProvider[subjectType])
.applyDynamic($propertyNameExpr: String)(satisfying)
.$asInstanceOf$[SatisfyingNotEquals[T]] } => '{
$typeProvider
.applyDynamic($propertyNameExpr)(satisfying.notEquals[T]($obj, $ord))
.$asInstanceOf$[AssertionProperty]
}
}
/** Entrypoint. This macro method takes an expression composed of /** Entrypoint. This macro method takes an expression composed of
* AssertionProperty expressions and rewrites them into actual assertions. * AssertionProperty expressions and rewrites them into actual assertions.
*/ */
@ -70,18 +89,12 @@ object GenerativeFluentAssertionsDSL:
// the should have assertion. Again, the applyDynamic is explicit. // the should have assertion. Again, the applyDynamic is explicit.
// The quoted pattern also extracts: the type of the assertion subject, // The quoted pattern also extracts: the type of the assertion subject,
// and the type of the property (actually... not exactly that. Why?) // and the type of the property (actually... not exactly that. Why?)
case '{
(${ typeProvider }: HavePropertyTypeProvider[subjectType])
.applyDynamic($propertyNameExpr: String)(satisfying)
.$asInstanceOf$[SatisfyingNotEquals[fieldType]]
.`!==`($property)
} =>
'{}
case '{ case '{
(${ typeProvider }: HavePropertyTypeProvider[subjectType]) (${ typeProvider }: HavePropertyTypeProvider[subjectType])
.applyDynamic($propertyNameExpr: String)($valueExpr: valueType => Boolean) .applyDynamic($propertyNameExpr: String)($valueExpr: valueType => Boolean)
.$asInstanceOf$[AssertionProperty] .$asInstanceOf$[AssertionProperty]
} => } =>
println(assertionExpr.show)
val subjectExpr = '{ $typeProvider.subject } val subjectExpr = '{ $typeProvider.subject }
generateShouldHaveAssertion[subjectType, valueType]( generateShouldHaveAssertion[subjectType, valueType](
subjectExpr, subjectExpr,

View file

@ -17,12 +17,13 @@ end DynamicShouldBeProperty
* and thus has lower precedence than alphanumeric operators. Therefore, we must handle "!==" as the final link of * 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. * a method chain instead of a predicate builder on `satisfying`, like the other operators.
*/ */
sealed trait SatisfyingNotEquals[T]: sealed trait SatisfyingNotEquals[T]
def `!==`(toWhat: T): AssertionProperty = ???
case object satisfying: case object satisfying:
def `===`[T](toWhat: T)(using ord: Ordering[T]): T => Boolean = def `===`[T](toWhat: T)(using ord: Ordering[T]): T => Boolean =
something => ord.eq(something, toWhat) something => ord.equiv(something, toWhat)
def notEquals[T](toWhat: T, ord: Ordering[T]): T => Boolean =
something => !ord.equiv(something, toWhat)
def `<`[T](toWhat: T)(using ord: Ordering[T]): T => Boolean = def `<`[T](toWhat: T)(using ord: Ordering[T]): T => Boolean =
something => ord.lt(something, toWhat) something => ord.lt(something, toWhat)
def `>`[T](toWhat: T)(using ord: Ordering[T]): T => Boolean = def `>`[T](toWhat: T)(using ord: Ordering[T]): T => Boolean =

View file

@ -41,11 +41,11 @@ case class Box(label: String, weight: Mass, price: Money)
(person should be_).adult (person should be_).adult
List(1) should have head satisfying === 1 List(1) should have head satisfying === 1
box should have weight satisfying === 3.kg box should have weight satisfying === 30.kg
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 >= 3.0.kg
/* should have assertions */ /* should have assertions */
// // assert(List(1).head == 1, ...) // // assert(List(1).head == 1, ...)