146 lines
3.7 KiB
Scala
146 lines
3.7 KiB
Scala
package green.thisfieldwas.embracingnondeterminism.data
|
|
|
|
import green.thisfieldwas.embracingnondeterminism.control.ApplicativeLaws
|
|
import green.thisfieldwas.embracingnondeterminism.util._
|
|
import org.scalacheck.Gen
|
|
import org.scalatest.Inside
|
|
import org.scalatest.matchers.should.Matchers
|
|
import org.scalatest.wordspec.AnyWordSpec
|
|
|
|
class OptionSpec extends AnyWordSpec with Matchers with Inside {
|
|
|
|
"Option" can {
|
|
"get" which {
|
|
"returns the value" when {
|
|
"the option is defined" in {
|
|
val option = Option(1)
|
|
option.get shouldBe 1
|
|
}
|
|
}
|
|
"faults" when {
|
|
"the option is not defined" in {
|
|
val option = Option[Int]()
|
|
inside(the[NoSuchElementException].thrownBy(option.get)) { case e =>
|
|
e.getMessage shouldBe "None.get"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
"isSome" which {
|
|
"is true" when {
|
|
"the option is defined" in {
|
|
val option = Option(1)
|
|
option.isSome shouldBe true
|
|
}
|
|
}
|
|
"is false" when {
|
|
"the option is not defined" in {
|
|
val option = Option[Int]()
|
|
option.isSome shouldBe false
|
|
}
|
|
}
|
|
}
|
|
|
|
"isNone" when {
|
|
"is true" when {
|
|
"the option is not defined" in {
|
|
val option = Option[Int]()
|
|
option.isNone shouldBe true
|
|
}
|
|
}
|
|
"is false" when {
|
|
"the option is defined" in {
|
|
val option = Option(1)
|
|
option.isNone shouldBe false
|
|
}
|
|
}
|
|
}
|
|
|
|
"fold" which {
|
|
"'folds' both cases of absent or present instances of term `A` into a new `B`" when {
|
|
"the instance of term `A` is present" in {
|
|
Option(123).fold("absent")(_.toString) shouldBe "123"
|
|
}
|
|
"the instance of term `A` is absent" in {
|
|
Option[Int]().fold("absent")(_.toString) shouldBe "absent"
|
|
}
|
|
}
|
|
}
|
|
|
|
"orElse" which {
|
|
"picks the current option if it is non-empty" in {
|
|
Some(4).orElse(Some(5)) shouldBe Some(4)
|
|
Some(4).orElse(None) shouldBe Some(4)
|
|
}
|
|
"picks the other option if the current is empty" in {
|
|
None.orElse(Some(4)) shouldBe Some(4)
|
|
None.orElse(None) shouldBe None
|
|
}
|
|
}
|
|
}
|
|
|
|
"Option object" can {
|
|
"apply" which {
|
|
"creates a Some" when {
|
|
"given a value" in {
|
|
Option(1) shouldBe Some(1)
|
|
}
|
|
}
|
|
"creates a None" when {
|
|
"given no argument" in {
|
|
Option() shouldBe None
|
|
}
|
|
}
|
|
}
|
|
|
|
"cond" which {
|
|
"conditionally creates a Some" when {
|
|
"the condition is true" in {
|
|
Option.cond(test = true)("this will be lifted into a Some") shouldBe Some("this will be lifted into a Some")
|
|
}
|
|
"the condition is false" in {
|
|
Option.cond(test = false)("this will not be lifted") shouldBe None
|
|
}
|
|
}
|
|
}
|
|
|
|
"unapply" which {
|
|
"matches Some" in {
|
|
Some(1) match {
|
|
case Option(x) => x shouldBe 1
|
|
case _ => fail("unapply didn't match")
|
|
}
|
|
}
|
|
"doesn't match None" in {
|
|
None match {
|
|
case Option(_) => fail("unapply matched")
|
|
case _ =>
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Check that `Option` conforms to typeclass laws.
|
|
*/
|
|
class OptionLaws extends Laws with FunctorLaws with ApplicativeLaws {
|
|
|
|
/** From a generator for some type `A`, generate in roughly equal proportions
|
|
* a `None` of an absence of `A` or a `Some` of a presence of `A`.
|
|
*/
|
|
implicit val optionLiftedGen: LiftedGen[Option] = new LiftedGen[Option] {
|
|
|
|
override def lift[A](gen: Gen[A]): Gen[Option[A]] =
|
|
Gen.lzy(
|
|
Gen.oneOf(
|
|
Gen.const(None),
|
|
gen.map(Some(_)),
|
|
)
|
|
)
|
|
}
|
|
|
|
checkFunctorLaws[Option]()
|
|
checkApplicativeLaws[Option]()
|
|
}
|