unbeliever-0.10.0.7: tests/CheckRopeBehaviour.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
module CheckRopeBehaviour
( checkRopeBehaviour,
main,
)
where
import Core.System (finally)
import Core.Text.Rope
import Core.Text.Utilities
import Data.Char (isSpace)
import qualified Data.FingerTree as F
import Data.Hashable (hash)
import qualified Data.List as List
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.Text.Lazy as U
import qualified Data.Text.Short as S
import Test.Hspec
main :: IO ()
main = do
finally (hspec checkRopeBehaviour) (putStrLn ".")
hydrogen = "H₂" :: Rope
sulfate = "SO₄" :: Rope
sulfuric_acid = hydrogen <> sulfate
compound = "3" <> "-" <> "ethyl" <> "-" <> "4" <> "-" <> "methyl" <> "hexane" :: Rope
checkRopeBehaviour :: Spec
checkRopeBehaviour = do
describe "Rope data type" $ do
it "knows what a singleton is" $ do
singletonRope 'i' `shouldBe` "i"
it "IsString instance behaves" $ do
unRope ("Hello" :: Rope) `shouldBe` F.singleton (S.pack "Hello")
it "calculates length accurately" $ do
widthRope hydrogen `shouldBe` 2
widthRope sulfate `shouldBe` 3
widthRope (hydrogen <> sulfate) `shouldBe` 5
it "Eq instance behaves" $ do
("" :: Rope) == ("" :: Rope) `shouldBe` True
("C" :: Rope) /= ("" :: Rope) `shouldBe` True
("" :: Rope) /= ("F" :: Rope) `shouldBe` True
("O" :: Rope) == ("O" :: Rope) `shouldBe` True
("H₂" :: Rope) == ("H₂" :: Rope) `shouldBe` True
("H₂" :: Rope) /= ("SO₄" :: Rope) `shouldBe` True
it "Hashable instance behaves" $ do
hash ("Hello" :: Rope) `shouldBe` hash (singletonRope 'H' <> intoRope ("ello" :: String))
-- depended on Textual instance for String being fixed and
-- the Eq instance being customized to ignore tree structure
it "concatonates two Ropes correctly (Monoid)" $ do
("H₂" :: Rope) <> ("SO₄" :: Rope) `shouldBe` ("H₂SO₄" :: Rope)
it "concatonates two Ropes correctly (Textual)" $ do
appendRope ("SO₄" :: Rope) ("H₂" :: Rope) `shouldBe` ("H₂SO₄" :: Rope)
it "replicates itself" $ do
replicateRope 3 "hello" `shouldBe` ("hellohellohello" :: Rope)
length (unRope (replicateRope 3 "hello")) `shouldBe` 3
replicateRope 3 "" `shouldBe` emptyRope
replicateRope 0 "hello" `shouldBe` emptyRope
replicateChar 3 'x' `shouldBe` ("xxx" :: Rope)
replicateChar 0 'x' `shouldBe` ("" :: Rope)
it "exports to ByteString" $
let expected = T.encodeUtf8 (T.pack "H₂SO₄")
in do
fromRope sulfuric_acid `shouldBe` expected
it "exports to Text (Strict)" $ do
fromRope sulfuric_acid `shouldBe` T.pack "H₂SO₄"
it "exports to Text (Lazy)" $ do
fromRope sulfuric_acid `shouldBe` U.pack "H₂SO₄"
it "does the splits" $ do
-- compare behaviour on Haskell lists
List.splitAt 0 ("123456789" :: String) `shouldBe` ("", "123456789")
List.splitAt 3 ("123456789" :: String) `shouldBe` ("123", "456789")
-- expect same behaviour of Rope
splitRope 0 ("123456789" :: Rope) `shouldBe` ("", "123456789")
splitRope 3 ("123456789" :: Rope) `shouldBe` ("123", "456789")
splitRope 9 ("123456789" :: Rope) `shouldBe` ("123456789", "")
splitRope 10 ("123456789" :: Rope) `shouldBe` ("123456789", "")
splitRope (-1) ("123456789" :: Rope) `shouldBe` ("", "123456789")
-- exercise splitRopeting at and between piece boundaries
splitRope 0 compound `shouldBe` ("", "3-ethyl-4-methylhexane")
splitRope 1 compound `shouldBe` ("3", "-ethyl-4-methylhexane")
splitRope 2 compound `shouldBe` ("3-", "ethyl-4-methylhexane")
splitRope 4 compound `shouldBe` ("3-et", "hyl-4-methylhexane")
-- 1234567890
splitRope 10 compound `shouldBe` ("3-ethyl-4-", "methylhexane")
splitRope 11 compound `shouldBe` ("3-ethyl-4-m", "ethylhexane")
splitRope 16 compound `shouldBe` ("3-ethyl-4-methyl", "hexane")
splitRope 21 compound `shouldBe` ("3-ethyl-4-methylhexan", "e")
widthRope compound `shouldBe` 22
splitRope 22 compound `shouldBe` ("3-ethyl-4-methylhexane", "")
splitRope 23 compound `shouldBe` ("3-ethyl-4-methylhexane", "")
splitRope (-1) compound `shouldBe` ("", "3-ethyl-4-methylhexane")
it "does insertion correctly" $ do
insertRope 3 "two" "onethree" `shouldBe` "onetwothree"
insertRope 3 "Con" "Def 1" `shouldBe` "DefCon 1"
insertRope 0 "one" "twothree" `shouldBe` "onetwothree"
insertRope 6 "three" "onetwo" `shouldBe` "onetwothree"
it "finds characters correctly" $ do
findIndexRope (== '3') compound `shouldBe` (Just 0)
findIndexRope (== '4') compound `shouldBe` (Just 8)
findIndexRope (== '!') compound `shouldBe` Nothing
findIndexRope (== 'e') compound `shouldBe` (Just 2)
describe "QuasiQuoted string literals" $ do
it "string literal is IsString" $ do
[quote|Hello|] `shouldBe` ("Hello" :: String)
[quote|Hello|] `shouldBe` ("Hello" :: Rope)
it "trims multi-line string literal" $ do
[quote|
Hello
|]
`shouldBe` ("Hello\n" :: Rope)
[quote|
Hello
World
|]
`shouldBe` ("Hello\nWorld\n" :: Rope)
describe "Splitting into words" $ do
it "breaks short text into chunks" $ do
intoChunks isSpace "" `shouldBe` []
intoChunks isSpace "Hello" `shouldBe` ["Hello"]
intoChunks isSpace "Hello World" `shouldBe` ["Hello", "World"]
intoChunks isSpace "Hello " `shouldBe` ["Hello", ""]
intoChunks isSpace " Hello" `shouldBe` ["", "Hello"]
intoChunks isSpace " Hello " `shouldBe` ["", "Hello", ""]
it "breaks consecutive short texts into chunks" $ do
intoPieces isSpace "Hello" (Nothing, [])
`shouldBe` (Just "Hello", [])
intoPieces isSpace "" (Nothing, [])
`shouldBe` (Nothing, [])
intoPieces isSpace "" (Nothing, ["World"])
`shouldBe` (Nothing, ["World"])
intoPieces isSpace "This is" (Nothing, ["", "a", "", "test."])
`shouldBe` (Just "This", ["is", "", "a", "", "test."])
intoPieces isSpace "This i" (Just "s", ["", "a", "", "test."])
`shouldBe` (Just "This", ["is", "", "a", "", "test."])
it "single piece containing multiple words splits correctly" $
let text = "This is a test"
in do
breakWords text `shouldBe` ["This", "is", "a", "test"]
it "single piece, long run of whitespace splits correctly" $
let text = "This is\na test"
in do
breakWords text `shouldBe` ["This", "is", "a", "test"]
it "text spanning two pieces can be split into words" $
let text = "This is " <> "a test"
in do
breakWords text `shouldBe` ["This", "is", "a", "test"]
it "text spanning many pieces can be split into words" $
let text = "st" <> "" <> "op" <> "" <> " " <> " " <> "and go" <> "op"
in do
breakWords text `shouldBe` ["stop", "and", "goop"]
it "empty and whitespace-only corner cases handled correctly" $
let text = " " <> "" <> "stop" <> "" <> " "
in do
breakWords text `shouldBe` ["stop"]
describe "Splitting into lines" $ do
it "preconditions are met" $ do
breakLines "" `shouldBe` []
breakLines "Hello" `shouldBe` ["Hello"]
breakLines "Hello\nWorld" `shouldBe` ["Hello", "World"]
breakLines "Hello\n" `shouldBe` ["Hello"]
breakLines "\nHello" `shouldBe` ["", "Hello"]
breakLines "\nHello\n" `shouldBe` ["", "Hello"]
breakLines "Hello\nWorld\n" `shouldBe` ["Hello", "World"]
breakLines "Hello\n\nWorld\n" `shouldBe` ["Hello", "", "World"]
breakLines "Hello\n\nWorld\n\n" `shouldBe` ["Hello", "", "World", ""]
it "single piece containing multiple lines splits correctly" $
let para =
[quote|
This is a test
of the Emergency
Broadcast
System, beeeeep
|]
in do
breakLines para
`shouldBe` [ "This is a test",
"of the Emergency",
"Broadcast",
"System, beeeeep"
]
it "preserves blank lines" $
let para =
[quote|
First line.
Third line.
|]
in do
breakLines para
`shouldBe` [ "First line.",
"",
"Third line."
]
describe "Formatting paragraphs" $ do
it "multi-line paragraph rewraps correctly" $
let para =
[quote|
Hello this is
a test
of the Emergency Broadcast System
|]
in wrap 20 para
`shouldBe` [quote|
Hello this is a test
of the Emergency
Broadcast System|]
describe "Lines and columns" $ do
it "calculate position of a given block" $ do
calculatePositionEnd "" `shouldBe` (1, 1)
calculatePositionEnd "Hello" `shouldBe` (1, 6)
calculatePositionEnd "Hello\nWorld" `shouldBe` (2, 6)
calculatePositionEnd "\nWorld" `shouldBe` (2, 6)
calculatePositionEnd "\n" `shouldBe` (2, 1)