From fa3972ec1b09ca5d08b7a6e45d20ebf742c62f5c Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Wed, 18 Dec 2024 14:01:08 +0100 Subject: [PATCH] ex2 works --- .../GenerativeFluentAssertionsDSL.scala | 27 ++++++++++++++----- .../GenerativeFluentAssertionsDSLSyntax.scala | 7 ++--- .../ch/usi/si/msde/edsl/lecture10/Main.scala | 4 +-- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/main/scala/ch/usi/si/msde/edsl/lecture10/GenerativeFluentAssertionsDSL.scala b/src/main/scala/ch/usi/si/msde/edsl/lecture10/GenerativeFluentAssertionsDSL.scala index 668c087..97fb7b9 100644 --- a/src/main/scala/ch/usi/si/msde/edsl/lecture10/GenerativeFluentAssertionsDSL.scala +++ b/src/main/scala/ch/usi/si/msde/edsl/lecture10/GenerativeFluentAssertionsDSL.scala @@ -8,6 +8,25 @@ import scala.language.postfixOps */ 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 * AssertionProperty expressions and rewrites them into actual assertions. */ @@ -70,18 +89,12 @@ object GenerativeFluentAssertionsDSL: // the should have assertion. Again, the applyDynamic is explicit. // The quoted pattern also extracts: the type of the assertion subject, // and the type of the property (actually... not exactly that. Why?) - case '{ - (${ typeProvider }: HavePropertyTypeProvider[subjectType]) - .applyDynamic($propertyNameExpr: String)(satisfying) - .$asInstanceOf$[SatisfyingNotEquals[fieldType]] - .`!==`($property) - } => - '{} case '{ (${ typeProvider }: HavePropertyTypeProvider[subjectType]) .applyDynamic($propertyNameExpr: String)($valueExpr: valueType => Boolean) .$asInstanceOf$[AssertionProperty] } => + println(assertionExpr.show) val subjectExpr = '{ $typeProvider.subject } generateShouldHaveAssertion[subjectType, valueType]( subjectExpr, diff --git a/src/main/scala/ch/usi/si/msde/edsl/lecture10/GenerativeFluentAssertionsDSLSyntax.scala b/src/main/scala/ch/usi/si/msde/edsl/lecture10/GenerativeFluentAssertionsDSLSyntax.scala index eaa57c8..9093084 100644 --- a/src/main/scala/ch/usi/si/msde/edsl/lecture10/GenerativeFluentAssertionsDSLSyntax.scala +++ b/src/main/scala/ch/usi/si/msde/edsl/lecture10/GenerativeFluentAssertionsDSLSyntax.scala @@ -17,12 +17,13 @@ end DynamicShouldBeProperty * 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. */ -sealed trait SatisfyingNotEquals[T]: - def `!==`(toWhat: T): AssertionProperty = ??? +sealed trait SatisfyingNotEquals[T] case object satisfying: 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 = something => ord.lt(something, toWhat) def `>`[T](toWhat: T)(using ord: Ordering[T]): T => Boolean = diff --git a/src/main/scala/ch/usi/si/msde/edsl/lecture10/Main.scala b/src/main/scala/ch/usi/si/msde/edsl/lecture10/Main.scala index 3aeb7b6..c912061 100644 --- a/src/main/scala/ch/usi/si/msde/edsl/lecture10/Main.scala +++ b/src/main/scala/ch/usi/si/msde/edsl/lecture10/Main.scala @@ -41,11 +41,11 @@ case class Box(label: String, weight: Mass, price: Money) (person should be_).adult 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(3) should have size satisfying !== 0 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 */ // // assert(List(1).head == 1, ...)