alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Scala example source code file (ZipperTest.scala)

This example Scala source code file (ZipperTest.scala) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Scala by Example" TM.

Learn more about this Scala project at its project page.

Java - Scala tags/keywords

affects, deleteleft, deleterightc, equal, gen, int, lengths, move, prop, short, some, stream, the, zipper

The ZipperTest.scala Scala example source code

package scalaz

import Scalaz._
import NonEmptyList.nels
import Zipper._
import scalaz.scalacheck.ScalazProperties._
import scalaz.scalacheck.ScalazArbitrary._
import org.scalacheck.Arbitrary
import org.scalacheck.Arbitrary.arbitrary
import org.scalacheck.{Gen, Prop}
import org.scalacheck.Prop.forAll

object ZipperTest extends SpecLite {

  "Zipper From Stream" ! forAll { (xs: Stream[Int]) =>
    (xs.toZipper map (_.toStream)).getOrElse(Stream()) === xs
  }

  "Mapping on a zipper should be the same as mapping on the elements of the stream" ! forAll { (xs: Stream[Int], a: Int) =>
    val fun: Int => Int = _ + a

    (
      for (z <- xs.toZipper; zM <- (xs map fun).toZipper) yield z.map(fun) must_===(zM)
    ) getOrElse { xs.length must_===(0) }
  }

  "Zipper Move Then To Stream" in check {
    val n = nels(1, 2, 3, 4)
    n.toZipper.move(2).map(_.toStream).exists(_ ==(n.stream))
  }

  "Next Affects Lengths" ! forAll { (xs: Stream[Int]) =>
    (
      for (z <- xs.toZipper; zn <- z.next)
        yield {
        (zn.lefts.length must_===(z.lefts.length + 1));
        (zn.rights.length must_===(z.rights.length - 1))
      }
    ) getOrElse { xs.length mustBe_<(2) }
  }

  "nextC moves focus or loops" ! forAll { z: Zipper[Int] =>
    val zn = z.nextC
    zn.toStream must_===(z.toStream)

    if (z.atEnd) zn.atStart must_==(true)
    else zn.index must_===(z.index + 1)
  }

  "Next changes focus, lefts and rights " ! forAll { (l: Stream[Int], f: Int, r: Stream[Int]) =>
    if (r.length > 0) {
      val nextZipper = zipper(l, f, r).next.get
      nextZipper.focus must_===(r(0))
      nextZipper.lefts must_===(f +: l)
      nextZipper.rights must_===(r.tail)
    } else {
      zipper(l, f, r).next.isEmpty must_==(true)
    }
  }

  "Zipper next returns Some when rights is nonempty, none otherwise." ! forAll { (l: Stream[Int], f: Int, r: Stream[Int]) =>
    if (r.length > 0) zipper(l, f, r).next.isDefined must_==(true)
    else zipper(l, f, r).next.isDefined must_==(false)
  }

  "Zipper nextOr returns a new zipper when used on empty rights or Some of next" ! forAll {
    (l: Stream[Int], f: Int, r: Stream[Int], alt: Zipper[Int]) =>

    val z = zipper(l, f, r)
    if (r.length > 0) {
      z.next must_===(Some(z.nextOr(alt)))
    } else {
      z.nextOr(alt) must_===(alt)
    }
  }

  "Previous Affects Lengths" ! forAll { (xs: Stream[Int]) =>
    (
      for (z <- xs.zipperEnd; zn <- z.previous)
        yield zn.lefts.length must_===(z.lefts.length - 1) and (zn.rights.length must_===(z.rights.length + 1))
    ) getOrElse { xs.length mustBe_<(2) }
  }

  "previousC moves focus or loops" ! forAll { z: Zipper[Int] =>
    val zp = z.previousC
    zp.toStream must_===(z.toStream)

    if (z.atStart) zp.atEnd must_==(true)
    else zp.index must_===(z.index - 1)
  }

  "Previous changes the focus, lefts and rights " ! forAll { (l: Stream[Int], f: Int, r: Stream[Int]) =>
    if (l.length > 0) {
      val prevZipper = zipper(l, f, r).previous.get
      prevZipper.focus must_===(l(0))
      prevZipper.lefts must_===(l.tail)
      prevZipper.rights must_===(f +: r)
    } else {
      zipper(l, f, r).previous.isDefined must_==(false)
    }
  }

  "Zipper previousOr returns a new zipper when used on empty rights or Some of next" ! forAll {
    (l: Stream[Int], f: Int, r: Stream[Int], alt: Zipper[Int]) =>

    val z = zipper(l, f, r)
    if (l.length > 0) {
      z.previous must_===(Some(z.previousOr(alt)))
    } else {
      z.previousOr(alt) must_===(alt)
    }
  }

  "Zipper tryPrevious returns Some of next or throws" ! forAll { (l: Stream[Int], f: Int, r: Stream[Int]) =>
    val z = zipper(l, f, r)
    if (l.length > 0) {
      z.previous must_===(Some(z.tryPrevious))
    } else {
      z.tryPrevious.mustThrowA[RuntimeException]
    }
  }

  def insertionTest(
      name: String,
      insertion: (Zipper[Int], Int) => Zipper[Int],
      pred: (Zipper[Int], Zipper[Int], Int) => Prop) = name ! forAll { (z: Zipper[Int], e: Int) =>

    val zi = insertion(z, e)
    pred(zi, z, e)
  }

  val leftAndFocusChanged: (Zipper[Int], Zipper[Int], Int) => Prop = { (zNew, zOld, newFocus) =>
    {zNew.focus must_===(newFocus)} and
    {zNew.lefts.head  must_===(zOld.focus)} and
    {zNew.lefts.tail must_===(zOld.lefts)} and
    {zNew.rights must_===(zOld.rights)}
  }

  val rightAndFocusChanged: (Zipper[Int], Zipper[Int], Int) => Prop = { (zNew, zOld, newFocus) =>
    {zNew.focus must_===(newFocus)} and
    {zNew.lefts must_===(zOld.lefts)} and
    {zNew.rights.head must_===(zOld.focus)} and
    {zNew.rights.tail must_===(zOld.rights)}
  }

  insertionTest("insertRight changes focus and appends to lefts", (z, e) => z.insertRight(e), leftAndFocusChanged)
  insertionTest("insert changes focus and appends to lefts",      (z, e) => z.insert(e),      leftAndFocusChanged)
  insertionTest("insertLeft changes focus and appends to lefts",  (z, e) => z.insertLeft(e),  rightAndFocusChanged)

  "DeleteRight Affects Lengths" ! forAll { (xs: Stream[Int]) =>
    (
      for (z <- xs.toZipper; zn <- z.deleteRight)
        yield zn.rights.length must_===(z.rights.length - 1)
    ) getOrElse {xs.length mustBe_<(2) }
  }

  "DeleteRightC Affects Lengths" ! forAll { (xs: Stream[Int]) =>
    (
      for (z <- xs.toZipper; zn <- z.deleteRightC)
        yield zn.rights.length must_===(z.rights.length - 1)
    ) getOrElse {xs.length mustBe_<(2) }
  }

  "deleteRightC moves the focus to the right or if not possible to the first element" ! forAll { z: Zipper[Int] =>
    (
      for {
        zd <- z.deleteRightC
      } yield {
        if (z.rights.length > 0) zd.focus must_===(z.rights(0))
        else                     zd.focus must_===(z.lefts.last)
      }
    ) getOrElse{ (z.lefts.isEmpty must_==(true)) and (z.rights.isEmpty must_==(true)) }
  }

  "deleteRightCOr should return Some of deleteLeftC or an alternative" ! forAll { (z: Zipper[Int], alt: Zipper[Int]) =>
    val zd = z.deleteRightCOr(alt)
    if (z.lefts.length == 0 && z.rights.length == 0) zd must_===(alt)
    else z.deleteRightC must_===(Some(zd))
  }

  "DeleteRight Affects Lengths and Moves Left if at end" ! forAll { (xs: Stream[Int]) =>
    (
      for (z <- xs.zipperEnd; zn <- z.deleteRight)
        yield zn.lefts.length must_===(z.lefts.length - 1)
    ) getOrElse ( xs.length mustBe_<(2) )
  }

  "deleteRight moves the focus to the right or if not possible left" ! forAll { z: Zipper[Int] =>
    (
      for {
        zd <- z.deleteRight
      } yield {
        if (z.rights.length > 0) zd.focus must_===(z.rights(0))
        else                     zd.focus must_===(z.lefts(0))
      }
    ) getOrElse{ (z.lefts.isEmpty must_==(true)) and (z.rights.isEmpty must_==(true)) }
  }

  "deleteRightOr should return Some of deleteLeft or an alternative" ! forAll { (z: Zipper[Int], alt: Zipper[Int]) =>
    val zd = z.deleteRightOr(alt)
    if (z.lefts.length == 0 && z.rights.length == 0)
      zd must_===(alt)
    else if (z.rights.length != 0)
      (zd.rights must_===(z.rights.tail)) and (zd.lefts must_===(z.lefts))
    else
      (zd.rights.isEmpty must_==(true)) and (zd.lefts must_===(z.lefts.tail))
  }

  "DeleteLeft Affects Lengths" ! forAll { (xs: Stream[Int]) =>
    (
      for (z <- xs.zipperEnd; zn <- z.deleteLeft)
        yield zn.lefts.length must_===(z.lefts.length - 1)
    ) getOrElse (xs.length mustBe_<(2))
  }

  "deleteLeft moves the focus to the left or if not possible right" ! forAll { z: Zipper[Int] =>
    (
      for {
        zd <- z.deleteLeft
      } yield {
        if (z.lefts.length > 0) zd.focus must_===(z.lefts(0))
        else                    zd.focus must_===(z.rights(0))
      }
    ) getOrElse((z.lefts.isEmpty must_==(true)) and (z.rights.isEmpty must_==(true)))
  }

  "DeleteLeftC Affects Lengths" ! forAll { (xs: Stream[Int]) =>
    (
      for (z <- xs.zipperEnd; zn <- z.deleteLeftC)
        yield zn.lefts.length must_===(z.lefts.length - 1)
    ) getOrElse (xs.length mustBe_<(2))
  }

  "deleteLeftC moves the focus to the left or if not possible to the last element" ! forAll { z: Zipper[Int] =>
    (
      for {
        zd <- z.deleteLeftC
      } yield {
        if (z.lefts.length > 0) zd.focus must_===(z.lefts(0))
        else                    zd.focus must_===(z.rights.last)
      }
    ) getOrElse((z.lefts.isEmpty must_==(true)) and (z.rights.isEmpty must_==(true)))
  }

  "deleteLeftCOr should return Some of deleteLeftC or an alternative" ! forAll { (z: Zipper[Int], alt: Zipper[Int]) =>
    val zd = z.deleteLeftCOr(alt)
    if (z.lefts.length == 0 && z.rights.length == 0) zd must_===(alt)
    else z.deleteLeftC must_===(Some(zd))
  }

  "deleteRightOr should return Some of deleteLeft or an alternative" ! forAll { (z: Zipper[Int], alt: Zipper[Int]) =>
    val zd = z.deleteRightOr(alt)
    if (z.lefts.length == 0 && z.rights.length == 0) zd must_===(alt)
    else if (z.rights.length != 0){
      zd.rights must_===(z.rights.tail)
      zd.lefts must_===(z.lefts)
    }else{
      zd.rights.isEmpty must_==(true)
      zd.lefts must_===(z.lefts.tail)
    }
  }

  "DeleteLeft Affects Lengths and Moves Right if at start" ! forAll { (xs: Stream[Int]) =>
    (
      for (z <- xs.toZipper; zn <- z.deleteLeft)
        yield zn.rights.length must_===(z.rights.length - 1)
    ) getOrElse (xs.length mustBe_<(2))
  }

  "deleteLeftOr should return Some of deleteLeft or an alternative" ! forAll { (z: Zipper[Int], alt: Zipper[Int]) =>
    if (z.lefts.length == 0 && z.rights.length == 0) z.deleteLeftOr(alt) must_===(alt)
    else {
      val zd = z.deleteLeftOr(alt)
      if (z.lefts.length != 0){
        zd.lefts must_===(z.lefts.tail)
        zd.rights must_===(z.rights)
      }else{
        zd.lefts.isEmpty must_==(true)
        zd.rights must_===(z.rights.tail)
      }
    }
  }

  "DeleteRightC Affects Lengths and Cycles to Start if at end" ! forAll { (xs: Stream[Int]) =>
    (
      for (z <- xs.zipperEnd; zn <- z.deleteRightC)
        yield zn.rights.length must_===(z.lefts.length - 1)
    ) getOrElse (xs.length mustBe_<(2))
  }

  "DeleteLeftC Affects Lengths and Cycles to end if at start" ! forAll { (xs: Stream[Int]) =>
    (
      for (z <- xs.toZipper; zn <- z.deleteLeftC)
        yield zn.lefts.length must_===(z.rights.length - 1)
    ) getOrElse (xs.length mustBe_<(2))
  }

  "Move" ! forAll { (xs: Stream[Int], ys: Stream[Int], f: Int, n: Short) =>

    zipper(xs, f, ys).move(n) map { (z: Zipper[Int]) =>
      z.lefts.length must_===(xs.length + n)
      z.rights.length must_===(ys.length - n)
      if(n > 0)
        ys(n - 1) must_===(z.focus)
      else if(n < 0)
        xs(-(n + 1)) must_===(z.focus)
      else
        f must_===(z.focus)
    } getOrElse {
      val okay = xs.length < -n || ys.length < n
      okay must_==(true)
    }
  }

  "move should not cause a stackoverflow error" in {
    val size = 32 * 1024
    val n = size - 1

    val f = for {
      z <- Stream.from(1).take(size).toZipper
      zm <- z.move(n)
    } yield zm.focus

    f must_===(Some(size))
  }

  "moveOr should return some of move or an alternative" ! forAll {
    (l: Stream[Int], f: Int, r: Stream[Int], n: Short, alt: Zipper[Int]) =>

    val z = zipper(l, f, r).moveOr(n, alt)
    if (l.length < (-n) || r.length < n) z must_===(alt)
    else {
      z.lefts.length must_===(l.length + n)
      z.rights.length must_===(r.length - n)
      if(n > 0)
        r(n - 1) must_===(z.focus)
      else if(n < 0)
        l(-(n + 1)) must_===(z.focus)
      else
        f must_===(z.focus)
    }
  }

  "Length should return the size of the zipper" ! forAll { (l: Stream[Int], f: Int, r: Stream[Int]) =>
    zipper(l, f, r).length must_===(l.length + 1 + r.length)
  }

  "The zipper should be atStart when the lefts stream is empty" ! forAll { (l: Stream[Int], f: Int, r: Stream[Int]) =>
    if (zipper(l, f, r).atStart) l.isEmpty must_==(true)
    else l.isEmpty must_==(false)
  }

  "withFocus should pair only the focus with true, false otherwise" ! forAll { z: Zipper[Int] =>
    val zf = z.withFocus
    zf.lefts.find(_._2).isEmpty must_==(true)
    zf.focus._2 must_==(true)
    zf.rights.find(_._2).isEmpty must_==(true)
  }

  "start should set the zipper at the start" ! forAll { z: Zipper[Int] =>
    val zs = z.start
    zs.toStream must_===(z.toStream)
    zs.index must_===(0)
  }

  "end should set the zipper at the end" ! forAll { z: Zipper[Int] =>
    val ze = z.end
    ze.toStream must_===(z.toStream)
    ze.index must_===(ze.length - 1)
  }

  "The zipper should be atEnd when the right stream is empty" ! forAll { (l: Stream[Int], f: Int, r: Stream[Int]) =>
    if (zipper(l, f, r).atEnd) r.isEmpty
    else !r.isEmpty
  }

  "Find" ! forAll { (xs: Stream[Int], ys: Stream[Int], f: Int, n: Int, m: Int) =>
    val p = (i: Int) => i < n && i > m
    zipper(xs, f, ys).findZ(p) map { z => p(z.focus) } getOrElse !(xs.find(p).isDefined || ys.find(p).isDefined || p(f))
  }

  "findZ shouldn't change elements" ! forAll { (xs: Stream[Int], ys: Stream[Int], f: Int, n: Int, m: Int) =>
    val p = (i: Int) => i < n && i > m
    zipper(xs, f, ys).findZ(p).map {
      z => z.toStream == zipper(xs, f, ys).toStream
    } getOrElse !(xs.find(p).isDefined || ys.find(p).isDefined || p(f))
  }

  "findZor returns some of find or an alternative" ! forAll {
    (z: Zipper[Int], n: Int, m: Int, alt: Zipper[Int]) =>
    val p = (i: Int) => i < n && i > m

    if (z.lefts.find(p).isDefined || p(z.focus) || z.rights.find(p).isDefined) {
      p(z.findZor(p, alt).focus) must_==(true)
    } else {
      z.findZor(p, alt) must_===(alt)
    }
  }

  "findZ should not cause a stackoverflow error" in {
    val size = 32 * 1024
    val elem = size - 1
    val r = for {
      z <- Stream.from(1).take(size).toZipper
      zf <- z.findZ(_ == elem)
    } yield zf.focus

    r must_===(Some(elem))
  }

  "findBy if given a function that returns None should not return anything" ! forAll { z: Zipper[Int] =>
    z.findBy(z => None)(x => x == z.focus).isEmpty
  }

  val intZipperWithExistingElement: Gen[(Zipper[Int], Int)] = for {
    z <- arbitrary[Zipper[Int]]
    stream = z.toStream
    i <- Gen.choose(0, stream.length -1)
  } yield (z, stream(i))

  "given nextC findBy should return Some if the element exists" !  forAll(intZipperWithExistingElement) { case (z, e) =>
    z.findBy(z => some(z.nextC))(x => x == e).isDefined
   }

  "findBy should not blow the stack" !  prop { z: Zipper[Int] =>
    var limit = 10 * 1000
    z.findBy(z => if (limit > 0) { limit -= 1; some(z.nextC) } else none)(x => false)
    true
   }

  def minSizeIntZipper(size: Int): Gen[Zipper[Int]] = for {
      leftSize <- Gen.choose(0, size - 2)
      rightSize = size - 1 - leftSize
      lefts  <- Gen.containerOfN[Stream,Int](leftSize,  implicitly[Arbitrary[Int]].arbitrary)
      rights <- Gen.containerOfN[Stream,Int](rightSize, implicitly[Arbitrary[Int]].arbitrary)
      focus <- arbitrary[Int]
  } yield zipper(lefts, focus, rights)

  "findNext should not blow the stack" !  forAll(minSizeIntZipper(10 * 1000)) { z =>
    var limit = 10 * 1000
    z.start.findNext { x => limit -= 1; limit > 0 }
    true
   }

  "findPrevious should not blow the stack" !  forAll(minSizeIntZipper(10 * 1000)) { z =>
    var limit = 10 * 1000
    z.end.findPrevious { x => limit -= 1; limit > 0 }
    true
   }

  "Update Modifies Zipper Correctly" ! forAll { (xs: Stream[Int], ys: Stream[Int], f: Int, u: Int) =>
    zipper(xs, f, ys).update(u) must_===(zipper(xs, u, ys))
  }

  "Modify Modifies Zipper Correctly" ! forAll { (xs: Stream[Int], ys: Stream[Int], f: Int, u: Int) =>
    val modF: Int => Int = _ + u
    zipper(xs, f, ys).modify(modF) must_===(zipper(xs, f + u, ys))
  }

  "Start" ! forAll { (xs: Stream[Int], ys: Stream[Int], f: Int) =>
    val zo = zipper(xs, f, ys)
    val z = zo.start

    z.lefts.length must_===(0)
    z.rights.length must_===(z.length - 1)
    zo.move(-xs.length) must_===(Some(z))
    ((z.move(xs.length) == Some(zo)) || z.length == 0) must_==(true)
  }

  "End" ! forAll { (xs: Stream[Int], ys: Stream[Int], f: Int) =>
    val zo = zipper(xs, f, ys)
    val z = zo.end

    z.lefts.length must_===(z.length - 1)
    z.rights.length must_===(0)
    zo.move(ys.length) must_===(Some(z))
    (z.move(-ys.length) == Some(zo) || (z.length == 0)) must_==(true)
  }

  "positions should return a zippers with focus on this" ! forAll { z: Zipper[Int] =>
    z.positions.focus must_===(z)
  }

  "positions should return a zippers with all possible positions of a zipper" ! forAll { z: Zipper[Int] =>
    val indeces = z.positions.map { _.index }.toStream
    indeces.min must_===(0)
    indeces.max must_===(z.length -1)
    indeces.sorted must_===(indeces)
    z.positions.map { _.toStream }.toStream.distinct.length must_===(1)
  }

  "index returns the position of the focus" ! forAll { (l: Stream[Int], f: Int, r: Stream[Int]) =>
    zipper(l, f, r).index must_===(l.length)
  }

  checkAll("Zipper", equal.laws[Zipper[Int]])
  checkAll("Zipper", traverse1.laws[Zipper])
  checkAll("Zipper", FoldableTests.anyAndAllLazy[Zipper])
  checkAll("Zipper", comonad.laws[Zipper])

  {
    implicit def zipperEqual[A: Equal]: Equal[Zipper[A]] = new Equal[Zipper[A]] {
      import std.stream.streamEqual
      def streamEqualApprox = streamEqual[A].contramap((_: Stream[A]).take(1000))
      def equal(a1: Zipper[A], a2: Zipper[A]) =
        streamEqualApprox.equal(a1.lefts, a2.lefts) &&
          Equal[A].equal(a1.focus, a2.focus) &&
          streamEqualApprox.equal(a1.rights, a2.rights)
    }

    checkAll("Zipper", applicative.laws[Zipper])
  }
}

// vim: expandtab:ts=2:sw=2

Other Scala examples (source code examples)

Here is a short list of links related to this Scala ZipperTest.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.