/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

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