packages feed

balkon-0.3.0.0: test/Data/Text/ParagraphLayout/Internal/ZipperSpec.hs

module Data.Text.ParagraphLayout.Internal.ZipperSpec (spec) where

import Control.Monad (forM_)
import Data.Text (Text, empty, pack)
import qualified Data.Text as Text

import Test.Hspec
import qualified Data.Text.ParagraphLayout.Internal.Zipper as Zipper

sampleText :: Text
sampleText =
    Text.dropEnd 6 $
    Text.drop 4 $
    pack "xxx Příliš žluťoučký kůň úpěl ďábelské ódy. yyyyy"

sampleLength :: Int
sampleLength = 39

midPositions :: [Int]
midPositions = [1, 2, 5, 8, 38]

preMidPositions :: [Int]
preMidPositions = map pred midPositions

spec :: Spec
spec = do

    describe "start on empty text" $ do
        let z = Zipper.start empty
        it "is at start" $
            Zipper.atStart z `shouldBe` True
        it "is at end" $
            Zipper.atEnd z `shouldBe` True
        it "has nothing preceding it" $
            Zipper.preceding z `shouldBe` empty
        it "has nothing following it" $
            Zipper.following z `shouldBe` empty
        it "has no next character" $
            Zipper.next z `shouldBe` Nothing
        it "recombines into empty text" $
            Zipper.recombine z `shouldBe` empty
        it "unchanged by step" $
            Zipper.step z `shouldBe` z
        it "unchanged by advance" $
            Zipper.advanceBy 999 z `shouldBe` z

    describe "start" $ do
        let z = Zipper.start sampleText
        it "is at start" $
            Zipper.atStart z `shouldBe` True
        it "is not at end" $
            Zipper.atEnd z `shouldBe` False
        it "has nothing preceding it" $
            Zipper.preceding z `shouldBe` empty
        it "has everything following it" $
            Zipper.following z `shouldBe` sampleText
        it "has next character 'P'" $
            Zipper.next z `shouldBe` Just 'P'
        it "recombines into original text" $
            Zipper.recombine z `shouldBe` sampleText

    describe "split at zero" $ do
        let z = Zipper.splitAt 0 sampleText
        it "is at start" $
            Zipper.atStart z `shouldBe` True
        it "is not at end" $
            Zipper.atEnd z `shouldBe` False
        it "has nothing preceding it" $
            Zipper.preceding z `shouldBe` empty
        it "has everything following it" $
            Zipper.following z `shouldBe` sampleText
        it "has next character 'P'" $
            Zipper.next z `shouldBe` Just 'P'
        it "recombines into original text" $
            Zipper.recombine z `shouldBe` sampleText

    describe "split at negative value" $ do
        let z = Zipper.splitAt (-3) sampleText
        it "is at start" $
            Zipper.atStart z `shouldBe` True
        it "is not at end" $
            Zipper.atEnd z `shouldBe` False
        it "has nothing preceding it" $
            Zipper.preceding z `shouldBe` empty
        it "has everything following it" $
            Zipper.following z `shouldBe` sampleText
        it "has next character 'P'" $
            Zipper.next z `shouldBe` Just 'P'
        it "recombines into original text" $
            Zipper.recombine z `shouldBe` sampleText

    midPositions `forM_` \ n ->
        describe ("split at " ++ (show n)) $ do
            let z = Zipper.splitAt n sampleText
            it "is not at start" $
                Zipper.atStart z `shouldBe` False
            it "is not at end" $
                Zipper.atEnd z `shouldBe` False
            it ("preceding text has length " ++ show n) $
                Text.length (Zipper.preceding z) `shouldBe` n
            it ("following text has length " ++ show (sampleLength - n)) $
                Text.length (Zipper.following z) `shouldBe` (sampleLength - n)
            it "recombines into original text" $
                Zipper.recombine z `shouldBe` sampleText

    preMidPositions `forM_` \ n ->
        describe ("split at " ++ (show n) ++ " and step") $ do
            let z = Zipper.step $ Zipper.splitAt n sampleText
            let n' = n + 1
            it "is not at start" $
                Zipper.atStart z `shouldBe` False
            it "is not at end" $
                Zipper.atEnd z `shouldBe` False
            it ("preceding text has length " ++ show n') $
                Text.length (Zipper.preceding z) `shouldBe` n'
            it ("following text has length " ++ show (sampleLength - n')) $
                Text.length (Zipper.following z) `shouldBe` (sampleLength - n')
            it "recombines into original text" $
                Zipper.recombine z `shouldBe` sampleText

    describe "start and advance by 3" $ do
        let z = Zipper.advanceBy 3 $ Zipper.start sampleText
        it "should be the same as splitting at 3" $
            z `shouldBe` Zipper.splitAt 3 sampleText
        it "has next character 'l'" $
            Zipper.next z `shouldBe` Just 'l'
        it "recombines into original text" $
            Zipper.recombine z `shouldBe` sampleText

    describe "split at 4 and advance by 3" $ do
        let z = Zipper.advanceBy 3 $ Zipper.splitAt 4 sampleText
        it "should be the same as splitting at 7" $
            z `shouldBe` Zipper.splitAt 7 sampleText
        it "has next character z-caron" $
            Zipper.next z `shouldBe` Just 'ž'
        it "recombines into original text" $
            Zipper.recombine z `shouldBe` sampleText

    describe "split past text bounds" $ do
        let z = Zipper.splitAt 999 sampleText
        it "is not at start" $
            Zipper.atStart z `shouldBe` False
        it "is at end" $
            Zipper.atEnd z `shouldBe` True
        it "has everything preceding it" $
            Zipper.preceding z `shouldBe` sampleText
        it "has nothing following it" $
            Zipper.following z `shouldBe` empty
        it "has no next character" $
            Zipper.next z `shouldBe` Nothing
        it "recombines into original text" $
            Zipper.recombine z `shouldBe` sampleText

    describe "split at 3 and advance past text bounds" $ do
        let z = Zipper.advanceBy sampleLength $ Zipper.splitAt 3 sampleText
        it "is not at start" $
            Zipper.atStart z `shouldBe` False
        it "is at end" $
            Zipper.atEnd z `shouldBe` True
        it "has everything preceding it" $
            Zipper.preceding z `shouldBe` sampleText
        it "has nothing following it" $
            Zipper.following z `shouldBe` empty
        it "has no next character" $
            Zipper.next z `shouldBe` Nothing
        it "recombines into original text" $
            Zipper.recombine z `shouldBe` sampleText