/tests/src/test/scala/scalaz/ZipperTest.scala
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