Skip to content

Commit

Permalink
Merge pull request #605 from kevin-lee/task/603/ApproxFiniteDuration
Browse files Browse the repository at this point in the history
Close #603 - [`effectie-time`] Add `ApproxFiniteDuration` and `syntax`
  • Loading branch information
kevin-lee authored Jan 7, 2024
2 parents b45c5e0 + f39e6f1 commit 687db09
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package effectie.time

import cats.{Eq, Show}

import scala.concurrent.duration.FiniteDuration

/** @author Kevin Lee
* @since 2024-01-07
*/
final case class ApproxFiniteDuration(base: FiniteDuration, tolerance: FiniteDuration)
object ApproxFiniteDuration {
implicit val approxFiniteDurationEq: Eq[ApproxFiniteDuration] = Eq.fromUniversalEquals

implicit val approxFiniteDurationShow: Show[ApproxFiniteDuration] = {
case ApproxFiniteDuration(base, tolerance) =>
s"(${(base.minus(tolerance)).toString} to ${(base.plus(tolerance)).toString})"

}

implicit final class ApproxFiniteDurationOps(private val approxFiniteDuration: ApproxFiniteDuration) extends AnyVal {
def min: FiniteDuration = approxFiniteDuration.base.minus(approxFiniteDuration.tolerance)
def max: FiniteDuration = approxFiniteDuration.base.plus(approxFiniteDuration.tolerance)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package effectie.time

import scala.concurrent.duration.FiniteDuration

/** @author Kevin Lee
* @since 2024-01-07
*/
trait syntax {
implicit def FiniteDurationExtraOps(finiteDuration: FiniteDuration): syntax.FiniteDurationExtraOps =
new syntax.FiniteDurationExtraOps(finiteDuration)
}
object syntax extends syntax {
final class FiniteDurationExtraOps(private val finiteDuration: FiniteDuration) extends AnyVal {
def +-(tolerance: FiniteDuration): ApproxFiniteDuration =
ApproxFiniteDuration(finiteDuration, tolerance)

def isWithIn(approxFiniteDuration: ApproxFiniteDuration): Boolean =
finiteDuration >= approxFiniteDuration.min && finiteDuration <= approxFiniteDuration.max
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package effectie.time

import hedgehog._
import hedgehog.runner._

import scala.concurrent.duration._

/** @author Kevin Lee
* @since 2024-01-08
*/
object ApproxFiniteDurationSpec extends Properties {
override def tests: List[Test] = List(
property("test ApproxFiniteDuration.min and ApproxFiniteDuration.max", testMinMax)
)

def testMinMax: Property =
for {
base <- Gen.int(Range.linear(0, Int.MaxValue >> 1)).log("base")
tolerance <- Gen.int(Range.linear(0, Int.MaxValue >> 1)).log("tolerance")

approxFiniteDuration <- Gen
.constant(
ApproxFiniteDuration(base.seconds, tolerance.seconds)
)
.log("approxFiniteDuration")
} yield {
val expectedMin = (base - tolerance).seconds
val expectedMax = (base + tolerance).seconds
Result.all(
List(
(approxFiniteDuration.min ==== expectedMin).log(
s"""approxFiniteDuration.min is not equal to expectedMin
|approxFiniteDuration.min: ${approxFiniteDuration.min.toString}
| expectedMin: ${expectedMin.toString}
|""".stripMargin
),
(approxFiniteDuration.max ==== expectedMax).log(
s"""approxFiniteDuration.max is not equal to expectedMax
|approxFiniteDuration.max: ${approxFiniteDuration.max.toString}
| expectedMax: ${expectedMax.toString}
|""".stripMargin
),
)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package effectie.time

import cats.syntax.all._
import effectie.time.syntax._
import hedgehog._
import hedgehog.runner._

import scala.concurrent.duration._

/** @author Kevin Lee
* @since 2024-01-08
*/
object syntaxSpec extends Properties {

override def tests: List[Test] = List(
property("test FiniteDuration +- FiniteDuration to create ApproxFiniteDuration", testPlusMinus),
property("test FiniteDuration.isWithIn(ApproxFiniteDuration) with valid FiniteDuration", testIsWithInValid),
property("test FiniteDuration.isWithIn(ApproxFiniteDuration) with invalid FiniteDuration", testIsWithInInvalid),
)

def testPlusMinus: Property =
for {
tolerance <- Gen.int(Range.linear(0, Int.MaxValue >> 1)).log("tolerance")
base <- Gen.int(Range.linear(tolerance, Int.MaxValue >> 1)).log("base")

expected <- Gen
.constant(
ApproxFiniteDuration(base.seconds, tolerance.seconds)
)
.log("approxFiniteDuration")
} yield {
val baseDuration = base.seconds
val toleranceDuration = tolerance.seconds

val actual = baseDuration +- toleranceDuration
actual ==== expected
}

def testIsWithInValid: Property =
for {
tolerance <- Gen.int(Range.linear(0, Int.MaxValue >> 1)).log("tolerance")
base <- Gen.int(Range.linear(tolerance, Int.MaxValue >> 1)).log("base")

approx <- Gen
.constant(
ApproxFiniteDuration(base.seconds, tolerance.seconds)
)
.log("approxFiniteDuration")
} yield {
Result.all(
List(
{
val min = base - tolerance
Result.diffNamed(show"${min.toString}.seconds.isWithIn($approx) should be true", min.seconds, approx)(
_.isWithIn(_)
)
}, {
Result.diffNamed(show"${base.toString}.seconds.isWithIn($approx) should be true", base.seconds, approx)(
_.isWithIn(_)
)
}, {
val max = base + tolerance
Result.diffNamed(show"${max.toString}.seconds.isWithIn($approx) should be true", max.seconds, approx)(
_.isWithIn(_)
)
},
)
)
}

def testIsWithInInvalid: Property =
for {
tolerance <- Gen.int(Range.linear(0, Int.MaxValue >> 1 - 3)).log("tolerance")
base <- Gen.int(Range.linear(tolerance + 1, Int.MaxValue >> 1 - 2)).log("base")

approx <- Gen
.constant(
ApproxFiniteDuration(base.seconds, tolerance.seconds)
)
.log("approxFiniteDuration")
} yield {
Result.all(
List(
{
val min = base - tolerance - 1
Result.diffNamed(show"${min.toString}.seconds.isWithIn($approx) should be false", min.seconds, approx)(
!_.isWithIn(_)
)
}, {
val max = base + tolerance + 2
Result.diffNamed(show"${max.toString}.seconds.isWithIn($approx) should be false", max.seconds, approx)(
!_.isWithIn(_)
)
},
)
)
}

}

0 comments on commit 687db09

Please sign in to comment.