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" $ do
Zipper.atStart z `shouldBe` True
it "is at end" $ do
Zipper.atEnd z `shouldBe` True
it "has nothing preceding it" $ do
Zipper.preceding z `shouldBe` empty
it "has nothing following it" $ do
Zipper.following z `shouldBe` empty
it "has no next character" $ do
Zipper.next z `shouldBe` Nothing
it "recombines into empty text" $ do
Zipper.recombine z `shouldBe` empty
it "unchanged by step" $ do
Zipper.step z `shouldBe` z
it "unchanged by advance" $ do
Zipper.advanceBy 999 z `shouldBe` z
describe "start" $ do
let z = Zipper.start sampleText
it "is at start" $ do
Zipper.atStart z `shouldBe` True
it "is not at end" $ do
Zipper.atEnd z `shouldBe` False
it "has nothing preceding it" $ do
Zipper.preceding z `shouldBe` empty
it "has everything following it" $ do
Zipper.following z `shouldBe` sampleText
it "has next character 'P'" $ do
Zipper.next z `shouldBe` Just 'P'
it "recombines into original text" $ do
Zipper.recombine z `shouldBe` sampleText
describe "split at zero" $ do
let z = Zipper.splitAt 0 sampleText
it "is at start" $ do
Zipper.atStart z `shouldBe` True
it "is not at end" $ do
Zipper.atEnd z `shouldBe` False
it "has nothing preceding it" $ do
Zipper.preceding z `shouldBe` empty
it "has everything following it" $ do
Zipper.following z `shouldBe` sampleText
it "has next character 'P'" $ do
Zipper.next z `shouldBe` Just 'P'
it "recombines into original text" $ do
Zipper.recombine z `shouldBe` sampleText
describe "split at negative value" $ do
let z = Zipper.splitAt (-3) sampleText
it "is at start" $ do
Zipper.atStart z `shouldBe` True
it "is not at end" $ do
Zipper.atEnd z `shouldBe` False
it "has nothing preceding it" $ do
Zipper.preceding z `shouldBe` empty
it "has everything following it" $ do
Zipper.following z `shouldBe` sampleText
it "has next character 'P'" $ do
Zipper.next z `shouldBe` Just 'P'
it "recombines into original text" $ do
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" $ do
Zipper.atStart z `shouldBe` False
it "is not at end" $ do
Zipper.atEnd z `shouldBe` False
it ("preceding text has length " ++ show n) $ do
Text.length (Zipper.preceding z) `shouldBe` n
it ("following text has length " ++ show (sampleLength - n)) $ do
Text.length (Zipper.following z) `shouldBe` (sampleLength - n)
it "recombines into original text" $ do
Zipper.recombine z `shouldBe` sampleText
preMidPositions `forM_` \ n ->
describe ("split at " ++ (show n) ++ " and step") $ do
let z = Zipper.step $ Zipper.splitAt n sampleText
it "is not at start" $ do
Zipper.atStart z `shouldBe` False
it "is not at end" $ do
Zipper.atEnd z `shouldBe` False
it ("preceding text has length " ++ show (n + 1)) $ do
Text.length (Zipper.preceding z) `shouldBe` (n + 1)
it ("following text has length " ++ show (sampleLength - n - 1)) $ do
Text.length (Zipper.following z) `shouldBe` (sampleLength - n - 1)
it "recombines into original text" $ do
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" $ do
z `shouldBe` Zipper.splitAt 3 sampleText
it "has next character 'l'" $ do
Zipper.next z `shouldBe` Just 'l'
it "recombines into original text" $ do
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" $ do
z `shouldBe` Zipper.splitAt 7 sampleText
it "has next character z-caron" $ do
Zipper.next z `shouldBe` Just 'ž'
it "recombines into original text" $ do
Zipper.recombine z `shouldBe` sampleText
describe "split past text bounds" $ do
let z = Zipper.splitAt 999 sampleText
it "is not at start" $ do
Zipper.atStart z `shouldBe` False
it "is at end" $ do
Zipper.atEnd z `shouldBe` True
it "has everything preceding it" $ do
Zipper.preceding z `shouldBe` sampleText
it "has nothing following it" $ do
Zipper.following z `shouldBe` empty
it "has no next character" $ do
Zipper.next z `shouldBe` Nothing
it "recombines into original text" $ do
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" $ do
Zipper.atStart z `shouldBe` False
it "is at end" $ do
Zipper.atEnd z `shouldBe` True
it "has everything preceding it" $ do
Zipper.preceding z `shouldBe` sampleText
it "has nothing following it" $ do
Zipper.following z `shouldBe` empty
it "has no next character" $ do
Zipper.next z `shouldBe` Nothing
it "recombines into original text" $ do
Zipper.recombine z `shouldBe` sampleText