PageRenderTime 54ms CodeModel.GetById 15ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 0ms

/tests/src/test/scala/scalaz/ZipperTest.scala

http://github.com/scalaz/scalaz
Scala | 515 lines | 433 code | 81 blank | 1 comment | 111 complexity | fae90443f5bafb7571b3232822351738 MD5 | raw file
  1package scalaz
  2
  3import Scalaz._
  4import Zipper._
  5import scalaz.scalacheck.ScalazProperties._
  6import scalaz.scalacheck.ScalazArbitrary._
  7import org.scalacheck.Arbitrary
  8import org.scalacheck.Arbitrary.arbitrary
  9import org.scalacheck.{Gen, Prop}
 10import org.scalacheck.Prop.forAll
 11import scalaz.Maybe.just
 12
 13object ZipperTest extends SpecLite {
 14
 15  "Zipper From Stream" ! forAll { (xs: Stream[Int]) =>
 16    (xs.toZipper map (_.toStream)).getOrElse(Stream()) === xs
 17  }
 18
 19  "Mapping on a zipper should be the same as mapping on the elements of the stream" ! forAll { (xs: Stream[Int], a: Int) =>
 20    val fun: Int => Int = _ + a
 21
 22    (
 23      for (z <- xs.toZipper; zM <- (xs map fun).toZipper) yield z.map(fun) must_===(zM)
 24    ) getOrElse { xs.length must_===(0) }
 25  }
 26
 27  "Zipper Move Then To Stream" in check {
 28    val n = NonEmptyList(1, 2, 3, 4)
 29    n.toZipper.move(2).map(_.toStream).exists(_ ==(n.stream))
 30  }
 31
 32  "Next Affects Lengths" ! forAll { (xs: Stream[Int]) =>
 33    (
 34      for (z <- xs.toZipper; zn <- z.next)
 35        yield {
 36        (zn.lefts.length must_===(z.lefts.length + 1));
 37        (zn.rights.length must_===(z.rights.length - 1))
 38      }
 39    ) getOrElse { xs.length mustBe_<(2) }
 40  }
 41
 42  "nextC moves focus or loops" ! forAll { z: Zipper[Int] =>
 43    val zn = z.nextC
 44    zn.toStream must_===(z.toStream)
 45
 46    if (z.atEnd) zn.atStart must_==(true)
 47    else zn.index must_===(z.index + 1)
 48  }
 49
 50  "Next changes focus, lefts and rights " ! forAll { (l: Stream[Int], f: Int, r: Stream[Int]) =>
 51    if (r.length > 0) {
 52      val nextZipper = zipper(l, f, r).next.toOption.get
 53      nextZipper.focus must_===(r(0))
 54      nextZipper.lefts must_===(f +: l)
 55      nextZipper.rights must_===(r.tail)
 56    } else {
 57      zipper(l, f, r).next.isEmpty must_==(true)
 58    }
 59  }
 60
 61  "Zipper next returns Some when rights is nonempty, none otherwise." ! forAll { (l: Stream[Int], f: Int, r: Stream[Int]) =>
 62    if (r.length > 0) zipper(l, f, r).next.isJust must_==(true)
 63    else zipper(l, f, r).next.isJust must_==(false)
 64  }
 65
 66  "Zipper nextOr returns a new zipper when used on empty rights or Some of next" ! forAll {
 67    (l: Stream[Int], f: Int, r: Stream[Int], alt: Zipper[Int]) =>
 68
 69    val z = zipper(l, f, r)
 70    if (r.length > 0) {
 71      z.next must_=== just(z.nextOr(alt))
 72    } else {
 73      z.nextOr(alt) must_===(alt)
 74    }
 75  }
 76
 77  "Previous Affects Lengths" ! forAll { (xs: Stream[Int]) =>
 78    (
 79      for (z <- xs.zipperEnd; zn <- z.previous)
 80        yield zn.lefts.length must_===(z.lefts.length - 1) and (zn.rights.length must_===(z.rights.length + 1))
 81    ) getOrElse { xs.length mustBe_<(2) }
 82  }
 83
 84  "previousC moves focus or loops" ! forAll { z: Zipper[Int] =>
 85    val zp = z.previousC
 86    zp.toStream must_===(z.toStream)
 87
 88    if (z.atStart) zp.atEnd must_==(true)
 89    else zp.index must_===(z.index - 1)
 90  }
 91
 92  "Previous changes the focus, lefts and rights " ! forAll { (l: Stream[Int], f: Int, r: Stream[Int]) =>
 93    if (l.length > 0) {
 94      val prevZipper = zipper(l, f, r).previous.toOption.get
 95      prevZipper.focus must_===(l(0))
 96      prevZipper.lefts must_===(l.tail)
 97      prevZipper.rights must_===(f +: r)
 98    } else {
 99      zipper(l, f, r).previous.isJust must_== false
100    }
101  }
102
103  "Zipper previousOr returns a new zipper when used on empty rights or Some of next" ! forAll {
104    (l: Stream[Int], f: Int, r: Stream[Int], alt: Zipper[Int]) =>
105
106    val z = zipper(l, f, r)
107    if (l.length > 0) {
108      z.previous must_=== just(z.previousOr(alt))
109    } else {
110      z.previousOr(alt) must_===(alt)
111    }
112  }
113
114  "Zipper tryPrevious returns Some of next or throws" ! forAll { (l: Stream[Int], f: Int, r: Stream[Int]) =>
115    val z = zipper(l, f, r)
116    if (l.length > 0) {
117      z.previous must_=== just(z.tryPrevious)
118    } else {
119      z.tryPrevious.mustThrowA[RuntimeException]
120    }
121  }
122
123  def insertionTest(
124      name: String,
125      insertion: (Zipper[Int], Int) => Zipper[Int],
126      pred: (Zipper[Int], Zipper[Int], Int) => Prop) = name ! forAll { (z: Zipper[Int], e: Int) =>
127
128    val zi = insertion(z, e)
129    pred(zi, z, e)
130  }
131
132  val leftAndFocusChanged: (Zipper[Int], Zipper[Int], Int) => Prop = { (zNew, zOld, newFocus) =>
133    {zNew.focus must_===(newFocus)} and
134    {zNew.lefts.head  must_===(zOld.focus)} and
135    {zNew.lefts.tail must_===(zOld.lefts)} and
136    {zNew.rights must_===(zOld.rights)}
137  }
138
139  val rightAndFocusChanged: (Zipper[Int], Zipper[Int], Int) => Prop = { (zNew, zOld, newFocus) =>
140    {zNew.focus must_===(newFocus)} and
141    {zNew.lefts must_===(zOld.lefts)} and
142    {zNew.rights.head must_===(zOld.focus)} and
143    {zNew.rights.tail must_===(zOld.rights)}
144  }
145
146  insertionTest("insertRight changes focus and appends to lefts", (z, e) => z.insertRight(e), leftAndFocusChanged)
147  insertionTest("insert changes focus and appends to lefts",      (z, e) => z.insert(e),      leftAndFocusChanged)
148  insertionTest("insertLeft changes focus and appends to lefts",  (z, e) => z.insertLeft(e),  rightAndFocusChanged)
149
150  "DeleteRight Affects Lengths" ! forAll { (xs: Stream[Int]) =>
151    (
152      for (z <- xs.toZipper; zn <- z.deleteRight)
153        yield zn.rights.length must_===(z.rights.length - 1)
154    ) getOrElse {xs.length mustBe_<(2) }
155  }
156
157  "DeleteRightC Affects Lengths" ! forAll { (xs: Stream[Int]) =>
158    (
159      for (z <- xs.toZipper; zn <- z.deleteRightC)
160        yield zn.rights.length must_===(z.rights.length - 1)
161    ) getOrElse {xs.length mustBe_<(2) }
162  }
163
164  "deleteRightC moves the focus to the right or if not possible to the first element" ! forAll { z: Zipper[Int] =>
165    (
166      for {
167        zd <- z.deleteRightC
168      } yield {
169        if (z.rights.length > 0) zd.focus must_===(z.rights(0))
170        else                     zd.focus must_===(z.lefts.last)
171      }
172    ) getOrElse{ (z.lefts.isEmpty must_==(true)) and (z.rights.isEmpty must_==(true)) }
173  }
174
175  "deleteRightCOr should return Some of deleteLeftC or an alternative" ! forAll { (z: Zipper[Int], alt: Zipper[Int]) =>
176    val zd = z.deleteRightCOr(alt)
177    if (z.lefts.length == 0 && z.rights.length == 0) zd must_===(alt)
178    else z.deleteRightC must_=== just(zd)
179  }
180
181  "DeleteRight Affects Lengths and Moves Left if at end" ! forAll { (xs: Stream[Int]) =>
182    (
183      for (z <- xs.zipperEnd; zn <- z.deleteRight)
184        yield zn.lefts.length must_===(z.lefts.length - 1)
185    ) getOrElse ( xs.length mustBe_<(2) )
186  }
187
188  "deleteRight moves the focus to the right or if not possible left" ! forAll { z: Zipper[Int] =>
189    (
190      for {
191        zd <- z.deleteRight
192      } yield {
193        if (z.rights.length > 0) zd.focus must_===(z.rights(0))
194        else                     zd.focus must_===(z.lefts(0))
195      }
196    ) getOrElse{ (z.lefts.isEmpty must_==(true)) and (z.rights.isEmpty must_==(true)) }
197  }
198
199  "deleteRightOr should return Some of deleteLeft or an alternative" ! forAll { (z: Zipper[Int], alt: Zipper[Int]) =>
200    val zd = z.deleteRightOr(alt)
201    if (z.lefts.length == 0 && z.rights.length == 0)
202      zd must_===(alt)
203    else if (z.rights.length != 0)
204      (zd.rights must_===(z.rights.tail)) and (zd.lefts must_===(z.lefts))
205    else
206      (zd.rights.isEmpty must_==(true)) and (zd.lefts must_===(z.lefts.tail))
207  }
208
209  "DeleteLeft Affects Lengths" ! forAll { (xs: Stream[Int]) =>
210    (
211      for (z <- xs.zipperEnd; zn <- z.deleteLeft)
212        yield zn.lefts.length must_===(z.lefts.length - 1)
213    ) getOrElse (xs.length mustBe_<(2))
214  }
215
216  "deleteLeft moves the focus to the left or if not possible right" ! forAll { z: Zipper[Int] =>
217    (
218      for {
219        zd <- z.deleteLeft
220      } yield {
221        if (z.lefts.length > 0) zd.focus must_===(z.lefts(0))
222        else                    zd.focus must_===(z.rights(0))
223      }
224    ) getOrElse((z.lefts.isEmpty must_==(true)) and (z.rights.isEmpty must_==(true)))
225  }
226
227  "DeleteLeftC Affects Lengths" ! forAll { (xs: Stream[Int]) =>
228    (
229      for (z <- xs.zipperEnd; zn <- z.deleteLeftC)
230        yield zn.lefts.length must_===(z.lefts.length - 1)
231    ) getOrElse (xs.length mustBe_<(2))
232  }
233
234  "deleteLeftC moves the focus to the left or if not possible to the last element" ! forAll { z: Zipper[Int] =>
235    (
236      for {
237        zd <- z.deleteLeftC
238      } yield {
239        if (z.lefts.length > 0) zd.focus must_===(z.lefts(0))
240        else                    zd.focus must_===(z.rights.last)
241      }
242    ) getOrElse((z.lefts.isEmpty must_==(true)) and (z.rights.isEmpty must_==(true)))
243  }
244
245  "deleteLeftCOr should return Some of deleteLeftC or an alternative" ! forAll { (z: Zipper[Int], alt: Zipper[Int]) =>
246    val zd = z.deleteLeftCOr(alt)
247    if (z.lefts.length == 0 && z.rights.length == 0) zd must_===(alt)
248    else z.deleteLeftC must_=== just(zd)
249  }
250
251  "deleteRightOr should return Some of deleteLeft or an alternative" ! forAll { (z: Zipper[Int], alt: Zipper[Int]) =>
252    val zd = z.deleteRightOr(alt)
253    if (z.lefts.length == 0 && z.rights.length == 0) zd must_===(alt)
254    else if (z.rights.length != 0){
255      zd.rights must_===(z.rights.tail)
256      zd.lefts must_===(z.lefts)
257    }else{
258      zd.rights.isEmpty must_==(true)
259      zd.lefts must_===(z.lefts.tail)
260    }
261  }
262
263  "DeleteLeft Affects Lengths and Moves Right if at start" ! forAll { (xs: Stream[Int]) =>
264    (
265      for (z <- xs.toZipper; zn <- z.deleteLeft)
266        yield zn.rights.length must_===(z.rights.length - 1)
267    ) getOrElse (xs.length mustBe_<(2))
268  }
269
270  "deleteLeftOr should return Some of deleteLeft or an alternative" ! forAll { (z: Zipper[Int], alt: Zipper[Int]) =>
271    if (z.lefts.length == 0 && z.rights.length == 0) z.deleteLeftOr(alt) must_===(alt)
272    else {
273      val zd = z.deleteLeftOr(alt)
274      if (z.lefts.length != 0){
275        zd.lefts must_===(z.lefts.tail)
276        zd.rights must_===(z.rights)
277      }else{
278        zd.lefts.isEmpty must_==(true)
279        zd.rights must_===(z.rights.tail)
280      }
281    }
282  }
283
284  "DeleteRightC Affects Lengths and Cycles to Start if at end" ! forAll { (xs: Stream[Int]) =>
285    (
286      for (z <- xs.zipperEnd; zn <- z.deleteRightC)
287        yield zn.rights.length must_===(z.lefts.length - 1)
288    ) getOrElse (xs.length mustBe_<(2))
289  }
290
291  "DeleteLeftC Affects Lengths and Cycles to end if at start" ! forAll { (xs: Stream[Int]) =>
292    (
293      for (z <- xs.toZipper; zn <- z.deleteLeftC)
294        yield zn.lefts.length must_===(z.rights.length - 1)
295    ) getOrElse (xs.length mustBe_<(2))
296  }
297
298  "Move" ! forAll { (xs: Stream[Int], ys: Stream[Int], f: Int, n: Short) =>
299
300    zipper(xs, f, ys).move(n) map { (z: Zipper[Int]) =>
301      z.lefts.length must_===(xs.length + n)
302      z.rights.length must_===(ys.length - n)
303      if(n > 0)
304        ys(n - 1) must_===(z.focus)
305      else if(n < 0)
306        xs(-(n + 1)) must_===(z.focus)
307      else
308        f must_===(z.focus)
309    } getOrElse {
310      val okay = xs.length < -n || ys.length < n
311      okay must_==(true)
312    }
313  }
314
315  "move should not cause a stackoverflow error" in {
316    val size = 32 * 1024
317    val n = size - 1
318
319    val f = for {
320      z <- Stream.from(1).take(size).toZipper
321      zm <- z.move(n)
322    } yield zm.focus
323
324    f must_=== just(size)
325  }
326
327  "moveOr should return some of move or an alternative" ! forAll {
328    (l: Stream[Int], f: Int, r: Stream[Int], n: Short, alt: Zipper[Int]) =>
329
330    val z = zipper(l, f, r).moveOr(n, alt)
331    if (l.length < (-n) || r.length < n) z must_===(alt)
332    else {
333      z.lefts.length must_===(l.length + n)
334      z.rights.length must_===(r.length - n)
335      if(n > 0)
336        r(n - 1) must_===(z.focus)
337      else if(n < 0)
338        l(-(n + 1)) must_===(z.focus)
339      else
340        f must_===(z.focus)
341    }
342  }
343
344  "Length should return the size of the zipper" ! forAll { (l: Stream[Int], f: Int, r: Stream[Int]) =>
345    zipper(l, f, r).length must_===(l.length + 1 + r.length)
346  }
347
348  "The zipper should be atStart when the lefts stream is empty" ! forAll { (l: Stream[Int], f: Int, r: Stream[Int]) =>
349    if (zipper(l, f, r).atStart) l.isEmpty must_==(true)
350    else l.isEmpty must_==(false)
351  }
352
353  "withFocus should pair only the focus with true, false otherwise" ! forAll { z: Zipper[Int] =>
354    val zf = z.withFocus
355    zf.lefts.find(_._2).isEmpty must_==(true)
356    zf.focus._2 must_==(true)
357    zf.rights.find(_._2).isEmpty must_==(true)
358  }
359
360  "start should set the zipper at the start" ! forAll { z: Zipper[Int] =>
361    val zs = z.start
362    zs.toStream must_===(z.toStream)
363    zs.index must_===(0)
364  }
365
366  "end should set the zipper at the end" ! forAll { z: Zipper[Int] =>
367    val ze = z.end
368    ze.toStream must_===(z.toStream)
369    ze.index must_===(ze.length - 1)
370  }
371
372  "The zipper should be atEnd when the right stream is empty" ! forAll { (l: Stream[Int], f: Int, r: Stream[Int]) =>
373    if (zipper(l, f, r).atEnd) r.isEmpty
374    else !r.isEmpty
375  }
376
377  "Find" ! forAll { (xs: Stream[Int], ys: Stream[Int], f: Int, n: Int, m: Int) =>
378    val p = (i: Int) => i < n && i > m
379    zipper(xs, f, ys).findZ(p) map { z => p(z.focus) } getOrElse !(xs.find(p).isDefined || ys.find(p).isDefined || p(f))
380  }
381
382  "findZ shouldn't change elements" ! forAll { (xs: Stream[Int], ys: Stream[Int], f: Int, n: Int, m: Int) =>
383    val p = (i: Int) => i < n && i > m
384    zipper(xs, f, ys).findZ(p).map {
385      z => z.toStream == zipper(xs, f, ys).toStream
386    } getOrElse !(xs.find(p).isDefined || ys.find(p).isDefined || p(f))
387  }
388
389  "findZor returns some of find or an alternative" ! forAll {
390    (z: Zipper[Int], n: Int, m: Int, alt: Zipper[Int]) =>
391    val p = (i: Int) => i < n && i > m
392
393    if (z.lefts.find(p).isDefined || p(z.focus) || z.rights.find(p).isDefined) {
394      p(z.findZor(p, alt).focus) must_==(true)
395    } else {
396      z.findZor(p, alt) must_===(alt)
397    }
398  }
399
400  "findZ should not cause a stackoverflow error" in {
401    val size = 32 * 1024
402    val elem = size - 1
403    val r = for {
404      z <- Stream.from(1).take(size).toZipper
405      zf <- z.findZ(_ == elem)
406    } yield zf.focus
407
408    r must_=== just(elem)
409  }
410
411  "findBy if given a function that returns None should not return anything" ! forAll { z: Zipper[Int] =>
412    z.findBy(z => Maybe.empty)(x => x == z.focus).isEmpty
413  }
414
415  val intZipperWithExistingElement: Gen[(Zipper[Int], Int)] = for {
416    z <- arbitrary[Zipper[Int]]
417    stream = z.toStream
418    i <- Gen.choose(0, stream.length -1)
419  } yield (z, stream(i))
420
421  "given nextC findBy should return Some if the element exists" !  forAll(intZipperWithExistingElement) { case (z, e) =>
422    z.findBy(z => just(z.nextC))(x => x == e).isDefined
423   }
424
425  "findBy should not blow the stack" !  prop { z: Zipper[Int] =>
426    var limit = 10 * 1000
427    z.findBy(z => if (limit > 0) { limit -= 1; just(z.nextC) } else Maybe.empty)(x => false)
428    true
429   }
430
431  def minSizeIntZipper(size: Int): Gen[Zipper[Int]] = for {
432      leftSize <- Gen.choose(0, size - 2)
433      rightSize = size - 1 - leftSize
434      lefts  <- Gen.containerOfN[Stream,Int](leftSize,  implicitly[Arbitrary[Int]].arbitrary)
435      rights <- Gen.containerOfN[Stream,Int](rightSize, implicitly[Arbitrary[Int]].arbitrary)
436      focus <- arbitrary[Int]
437  } yield zipper(lefts, focus, rights)
438
439  "findNext should not blow the stack" !  forAll(minSizeIntZipper(10 * 1000)) { z =>
440    var limit = 10 * 1000
441    z.start.findNext { x => limit -= 1; limit > 0 }
442    true
443   }
444
445  "findPrevious should not blow the stack" !  forAll(minSizeIntZipper(10 * 1000)) { z =>
446    var limit = 10 * 1000
447    z.end.findPrevious { x => limit -= 1; limit > 0 }
448    true
449   }
450
451  "Update Modifies Zipper Correctly" ! forAll { (xs: Stream[Int], ys: Stream[Int], f: Int, u: Int) =>
452    zipper(xs, f, ys).update(u) must_===(zipper(xs, u, ys))
453  }
454
455  "Modify Modifies Zipper Correctly" ! forAll { (xs: Stream[Int], ys: Stream[Int], f: Int, u: Int) =>
456    val modF: Int => Int = _ + u
457    zipper(xs, f, ys).modify(modF) must_===(zipper(xs, f + u, ys))
458  }
459
460  "Start" ! forAll { (xs: Stream[Int], ys: Stream[Int], f: Int) =>
461    val zo = zipper(xs, f, ys)
462    val z = zo.start
463
464    z.lefts.length must_=== 0
465    z.rights.length must_===(z.length - 1)
466    zo.move(-xs.length) must_=== just(z)
467    ((z.move(xs.length) == just(zo)) || z.length == 0) must_== true
468  }
469
470  "End" ! forAll { (xs: Stream[Int], ys: Stream[Int], f: Int) =>
471    val zo = zipper(xs, f, ys)
472    val z = zo.end
473
474    z.lefts.length must_===(z.length - 1)
475    z.rights.length must_=== 0
476    zo.move(ys.length) must_=== just(z)
477    (z.move(-ys.length) == just(zo) || (z.length == 0)) must_== true
478  }
479
480  "positions should return a zippers with focus on this" ! forAll { z: Zipper[Int] =>
481    z.positions.focus must_===(z)
482  }
483
484  "positions should return a zippers with all possible positions of a zipper" ! forAll { z: Zipper[Int] =>
485    val indeces = z.positions.map { _.index }.toStream
486    indeces.min must_===(0)
487    indeces.max must_===(z.length -1)
488    indeces.sorted must_===(indeces)
489    z.positions.map { _.toStream }.toStream.distinct.length must_===(1)
490  }
491
492  "index returns the position of the focus" ! forAll { (l: Stream[Int], f: Int, r: Stream[Int]) =>
493    zipper(l, f, r).index must_===(l.length)
494  }
495
496  checkAll("Zipper", equal.laws[Zipper[Int]])
497  checkAll("Zipper", traverse1.laws[Zipper])
498  checkAll("Zipper", FoldableTests.anyAndAllLazy[Zipper])
499  checkAll("Zipper", comonad.laws[Zipper])
500
501  {
502    implicit def zipperEqual[A: Equal]: Equal[Zipper[A]] = new Equal[Zipper[A]] {
503      import std.stream.streamEqual
504      def streamEqualApprox = streamEqual[A].contramap((_: Stream[A]).take(1000))
505      def equal(a1: Zipper[A], a2: Zipper[A]) =
506        streamEqualApprox.equal(a1.lefts, a2.lefts) &&
507          Equal[A].equal(a1.focus, a2.focus) &&
508          streamEqualApprox.equal(a1.rights, a2.rights)
509    }
510
511    checkAll("Zipper", applicative.laws[Zipper])
512  }
513}
514
515// vim: expandtab:ts=2:sw=2