nbparts-0.1.0.0: tests/Tests/Util/TextSpec.hs
module Tests.Util.TextSpec where
import Data.Text qualified as Text
import Hedgehog (forAll, (===))
import Hedgehog.Gen qualified as Gen
import Hedgehog.Range qualified as Range
import Nbparts.Util.Text (lineColToIndex, replaceSlices, splitKeepNewlines)
import Test.Hspec (Spec, context, describe, it, shouldBe)
import Test.Hspec.Hedgehog (hedgehog)
spec :: Spec
spec = do
describe "lineColToIndex" $ do
context "when given an empty list" $ do
it "returns Nothing" $ hedgehog $ do
let textLines = []
line <- forAll $ Gen.int (Range.linear minBound maxBound)
col <- forAll $ Gen.int (Range.linear minBound maxBound)
lineColToIndex textLines line col === Nothing
context "when given a single line with valid line and col" $ do
it "returns `col - 1`" $ do
textLine <- forAll $ Gen.text (Range.linear 1 50) Gen.unicode
let textLines = [textLine]
col <- forAll $ Gen.int (Range.linear 1 (Text.length textLine + 1))
lineColToIndex textLines 1 col === Just (col - 1)
context "when given multiple lines with valid line and col" $ do
it "computes the correct index for a simple case" $ do
-- "abc\ndefg\nhijkl"
let textLines = ["abc", "defg", "hijkl"]
lineColToIndex textLines 1 1 `shouldBe` Just 0
lineColToIndex textLines 1 2 `shouldBe` Just 1
lineColToIndex textLines 2 1 `shouldBe` Just 4
lineColToIndex textLines 3 3 `shouldBe` Just 11
lineColToIndex textLines 3 6 `shouldBe` Just 14
it "returns an index whose letter is equal to the one retrieved using the line-col index" $ hedgehog $ do
textLines <-
forAll $
Gen.list
(Range.linear 1 5)
(Gen.text (Range.linear 1 50) Gen.unicode)
line <- forAll $ Gen.int (Range.linear 1 (length textLines))
let textLine = textLines !! (line - 1)
col <- forAll $ Gen.int (Range.linear 1 (Text.length textLine))
let letter = Text.index textLine (col - 1)
let text = Text.intercalate "\n" textLines
let maybeIdx = lineColToIndex textLines line col
let letter' = Text.index text <$> maybeIdx
Just letter === letter'
context "when given non-positive indices" $ do
it "returns Nothing" $ hedgehog $ do
textLines <-
forAll $
Gen.list
(Range.linear 1 5)
(Gen.text (Range.linear 1 50) Gen.unicode)
line <- forAll $ Gen.int (Range.linear (-100) 0)
col <- forAll $ Gen.int (Range.linear (-100) 0)
lineColToIndex textLines line col === Nothing
context "when given out-of-range line" $ do
it "returns Nothing" $ hedgehog $ do
textLines <-
forAll $
Gen.list
(Range.linear 1 5)
(Gen.text (Range.linear 1 50) Gen.unicode)
line <- forAll $ Gen.int (Range.linear (length textLines + 1) 1000)
lineColToIndex textLines line 1 === Nothing
context "when given out-of-range col" $ do
it "returns Nothing" $ hedgehog $ do
textLines <-
forAll $
Gen.list
(Range.linear 1 5)
(Gen.text (Range.linear 1 50) Gen.unicode)
line <- forAll $ Gen.int (Range.linear 1 (length textLines))
let textLine = textLines !! (line - 1)
col <- forAll $ Gen.int (Range.linear (Text.length textLine + 2) 1000)
lineColToIndex textLines line col === Nothing
describe "replaceSlices" $ do
context "when given no replacements" $ do
it "is identity" $ hedgehog $ do
t <- forAll $ Gen.text (Range.linear 0 250) Gen.unicode
replaceSlices t [] === Just t
context "when given only one replacement" $ do
it "behaves like manual splice" $ hedgehog $ do
t <- forAll $ Gen.text (Range.linear 1 100) Gen.unicode
start <- forAll $ Gen.int (Range.linear 0 (Text.length t))
end <- forAll $ Gen.int (Range.linear start (Text.length t))
repl <- forAll $ Gen.text (Range.linear 0 20) Gen.unicode
let manual = Text.take start t <> repl <> Text.drop end t
replaced = replaceSlices t [((start, end), repl)]
replaced === Just manual
context "when given only one replacement at the start" $ do
it "behaves like prepending" $ hedgehog $ do
t <- forAll $ Gen.text (Range.linear 1 100) Gen.unicode
repl <- forAll $ Gen.text (Range.linear 0 20) Gen.unicode
let replaced = replaceSlices t [((0, 0), repl)]
replaced === Just (repl <> t)
context "when given only one replacement at the end" $ do
it "behaves like appending" $ hedgehog $ do
t <- forAll $ Gen.text (Range.linear 1 100) Gen.unicode
repl <- forAll $ Gen.text (Range.linear 0 20) Gen.unicode
let len = Text.length t
let replaced = replaceSlices t [((len, len), repl)]
replaced === Just (t <> repl)
context "when given non-overlapping slices" $ do
it "correctly replaces" $ do
replaceSlices "abcdef" [((1, 3), "XYZ")] `shouldBe` Just "aXYZdef"
replaceSlices "abcdef" [((0, 1), "XYZ"), ((3, 6), "FOOBAR")] `shouldBe` Just "XYZbcFOOBAR"
replaceSlices "abcdef" [((0, 1), "XYZ"), ((1, 3), "FOO"), ((3, 6), "BAR")] `shouldBe` Just "XYZFOOBAR"
context "when given overlapping slices" $ do
it "returns Nothing" $ do
replaceSlices "abcdef" [((0, 4), "FOO"), ((1, 5), "BAR")] `shouldBe` Nothing
context "when given negative indices" $ do
it "returns Nothing" $ do
replaceSlices "abcdef" [((-1, 3), "FOO")] `shouldBe` Nothing
replaceSlices "abcdef" [((1, -1), "FOO")] `shouldBe` Nothing
replaceSlices "abcdef" [((-3, -1), "FOO")] `shouldBe` Nothing
context "when given out-of-range indices" $ do
it "returns Nothing" $ do
replaceSlices "abcdef" [((6, 8), "FOO")] `shouldBe` Nothing
replaceSlices "abcdef" [((3, 8), "FOO")] `shouldBe` Nothing
describe "splitKeepNewlines" $ do
context "when given empty text" $ do
it "returns an empty list" $ do
splitKeepNewlines "" `shouldBe` []
context "when given text intercalated with single newlines" $ do
it "splits and keeps newlines" $ do
splitKeepNewlines "abc\ndefg\nhijkl" `shouldBe` ["abc\n", "defg\n", "hijkl"]
context "when given text intercalated with multiple newlines" $ do
it "splits and keeps newlines" $ do
splitKeepNewlines "abc\n\ndefg\n\n\nhijkl" `shouldBe` ["abc\n", "\n", "defg\n", "\n", "\n", "hijkl"]
context "when given text with trailing newlines" $ do
it "splits and keeps newlines" $ do
splitKeepNewlines "abc\ndefg\n\nhijkl\n" `shouldBe` ["abc\n", "defg\n", "\n", "hijkl\n"]
splitKeepNewlines "abc\ndefg\n\nhijkl\n\n\n" `shouldBe` ["abc\n", "defg\n", "\n", "hijkl\n", "\n", "\n"]
it "concatenation of parts is the original text" $ hedgehog $ do
ls <-
forAll $
Gen.list
(Range.linear 0 10)
(Gen.text (Range.linear 0 100) Gen.unicode)
let txt = Text.intercalate "\n" ls
mconcat (splitKeepNewlines txt) === txt