From 9b1765f8bf7e2e3b8477ffd0bf0f38544f03b44e Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Tue, 17 Dec 2024 15:03:35 +0100 Subject: [PATCH] attempt --- .../GenerativeFluentAssertionsDSLSyntax.scala | 32 ++++++++++--------- .../ch/usi/si/msde/edsl/lecture10/Main.scala | 15 +++++---- .../edsl/lecture10/ShouldBeTypeProvider.scala | 1 - .../lecture10/ShouldHaveTypeProvider.scala | 26 ++++----------- 4 files changed, 32 insertions(+), 42 deletions(-) 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 83786ae..da4879c 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 @@ -12,20 +12,22 @@ class DynamicShouldBeProperty extends AssertionProperty with Dynamic: def applyDynamic(fieldName: String)(foo: Any*) = ??? end DynamicShouldBeProperty -object satisfying: - def `===`[T](toWhat: T)(using ord: Ordering[T]): T => Boolean = - something => ord.eq(something, toWhat) - def `!==`[T](toWhat: T)(using ord: Ordering[T]): T => Boolean = - something => ord.ne(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 = - 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) -end satisfying +case object satisfying + +case class AssertionCandidate[T](value: T, subject: String, propertyName: String): + private def comparison(other: T, predicate: (T, T) => Boolean, predicateOp: String): Unit = + assert( + predicate.apply(value, other), + s"${subject} did not have ${propertyName} ${predicateOp} ${other}, but it was equal to ${value}" + ) + + def `===`(other: T)(using ord: Ordering[T]): Unit = comparison(other, ord.eq(_, _), "==") + def `!==`(other: T)(using ord: Ordering[T]): Unit = comparison(other, ord.ne(_, _), "!=") + def `<=`(other: T)(using ord: Ordering[T]): Unit = comparison(other, ord.lteq, "<=") + def `>=`(other: T)(using ord: Ordering[T]): Unit = comparison(other, ord.gteq, ">=") + def `<`(other: T)(using ord: Ordering[T]): Unit = comparison(other, ord.lt, "<") + def `>`(other: T)(using ord: Ordering[T]): Unit = comparison(other, ord.gt, ">") +end AssertionCandidate class BePropertyTypeProvider[T](val subject: T) extends Selectable: def selectDynamic(fieldName: String): AssertionProperty = ??? @@ -44,7 +46,7 @@ end BePropertyTypeProvider * corresponding method def foo(arg: X): AssertionProperty. */ class HavePropertyTypeProvider[T](val subject: T) extends Selectable: - def applyDynamic(fieldName: String)(arg: Any): AssertionProperty = ??? + def applyDynamic(fieldName: String)(arg: Any): AssertionCandidate[T] = ??? end HavePropertyTypeProvider /** A trait for each type of assertion (be or have). 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 a926744..f38dc1c 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 @@ -25,13 +25,6 @@ case class Box(label: String, weight: Mass, price: Money) val box = Box("aBox", 30.kg, 10.CHF) assertions: - // { - // val p: BePropertyTypeProvider[Person] = new BePropertyTypeProvider[Person](person) - // p.asInstanceOf[BePropertyTypeProvider[Person] { - // def adult: AssertionProperty - // }].adult - // } - /* be.property assertions */ // assert(person.adult, ...) person should be.adult @@ -40,6 +33,14 @@ case class Box(label: String, weight: Mass, price: Money) // assert(List(1,2,3).nonEmpty) List(1, 2, 3) should be.nonEmpty + List(1) should have head satisfying === 1 + box should have weight satisfying === 3.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 >= 10.kg person should have age satisfying >= 10 // New syntax for `be` with type provider diff --git a/src/main/scala/ch/usi/si/msde/edsl/lecture10/ShouldBeTypeProvider.scala b/src/main/scala/ch/usi/si/msde/edsl/lecture10/ShouldBeTypeProvider.scala index 9b679c9..c78df5c 100644 --- a/src/main/scala/ch/usi/si/msde/edsl/lecture10/ShouldBeTypeProvider.scala +++ b/src/main/scala/ch/usi/si/msde/edsl/lecture10/ShouldBeTypeProvider.scala @@ -66,7 +66,6 @@ object ShouldBeTypeProvider: val p = BePropertyTypeProvider[T]($subject) p.asInstanceOf[tpe] } - println(res.show) res end ShouldBeTypeProvider diff --git a/src/main/scala/ch/usi/si/msde/edsl/lecture10/ShouldHaveTypeProvider.scala b/src/main/scala/ch/usi/si/msde/edsl/lecture10/ShouldHaveTypeProvider.scala index d2f7adf..d893ea9 100644 --- a/src/main/scala/ch/usi/si/msde/edsl/lecture10/ShouldHaveTypeProvider.scala +++ b/src/main/scala/ch/usi/si/msde/edsl/lecture10/ShouldHaveTypeProvider.scala @@ -29,26 +29,14 @@ object ShouldHaveTypeProvider: // Getting it as is returns an applied type with useless bounds (Nothing to Any), and the returned TypeRepr, // if re-applied to some `I` input type and `O` output type would be incompatible with `TypeRepr.of[I => O]` for // some reason - val function1TypeConstructor = AppliedType.unapply(TypeRepr.of[? => ?].asInstanceOf[AppliedType])._1 + val acTypeCtor = AppliedType.unapply(TypeRepr.of[AssertionCandidate[Unit]].asInstanceOf[AppliedType])._1 + val assertionCandidateType = AppliedType(acTypeCtor, List(fieldTypeRepr)) - val appliedType = AppliedType(function1TypeConstructor, List(fieldTypeRepr, TypeRepr.of[Boolean])) - - // Generates the "type" of the method to be generated for the refinement. - // The first parameter is the list of arguments, the second is a function returning - // the type of arguments, and the third one is a function returning the return type - // of the method. - // In this case: arg is the name of the parameter; - // _ => List(fieldTypeRepr) returns a list with the type of arg; - // _ => TypeRepr.of[AssertionProperty] returns the (reflection) type of the method. - val methodType = MethodType(List("arg"))( - _ => List(appliedType), - _ => TypeRepr.of[AssertionProperty] - ).widen - // returns the refinement of currentTypeRepr - - // symbol.name is the name of the method, - // methodType is its type. - val refinement = Refinement(currentTypeRepr, symbol.name, methodType) - refinement + val methodType = MethodType(List("satisfying"))( + _ => List(TypeRepr.of[satisfying.type]), + _ => assertionCandidateType + ) + Refinement(currentTypeRepr, symbol.name, methodType) /** Refines a type according to a list of fields or methods of arity 0. */