-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #605 from kevin-lee/task/603/ApproxFiniteDuration
Close #603 - [`effectie-time`] Add `ApproxFiniteDuration` and `syntax`
- Loading branch information
Showing
4 changed files
with
190 additions
and
0 deletions.
There are no files selected for viewing
24 changes: 24 additions & 0 deletions
24
modules/effectie-time/shared/src/main/scala/effectie/time/ApproxFiniteDuration.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
modules/effectie-time/shared/src/main/scala/effectie/time/syntax.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
|
||
} |
46 changes: 46 additions & 0 deletions
46
modules/effectie-time/shared/src/test/scala/effectie/time/ApproxFiniteDurationSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
), | ||
) | ||
) | ||
} | ||
} |
99 changes: 99 additions & 0 deletions
99
modules/effectie-time/shared/src/test/scala/effectie/time/syntaxSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(_) | ||
) | ||
}, | ||
) | ||
) | ||
} | ||
|
||
} |