gigaparsec-0.2.0.0: test/Text/Gigaparsec/CombinatorTests.hs
module Text.Gigaparsec.CombinatorTests where
import Test.Tasty
import Test.Tasty.HUnit
import Control.Monad
import Text.Gigaparsec
import Text.Gigaparsec.Char
import Text.Gigaparsec.Combinator
import Text.Gigaparsec.Internal.Test
tests :: TestTree
tests = testGroup "Combinator"
[ choiceTests
, optionTests
, decideTests
, fromMaybeSTests
, optionalTests
, manyNTests
, skipManyNTests
, sepByTests
, sepBy1Tests
, sepEndByTests
, sepEndBy1Tests
, endByTests
, endBy1Tests
, manyTillTests
, someTillTests
, countTests
, exactlyTests
, rangeTests
, range_Tests
, countRangeTests
]
choiceTests :: TestTree
choiceTests = testGroup "choice should"
[ testCase "fail if given the empty list" do ensureFails @() (choice []) ""
, testCase "behave like p for [p]" do
(choice [char 'a'] ~~ char 'a') ["", "a", "b"]
, testCase "parse in order" do
testParse (choice [string "a", string "b", string "bc"]) "bcd" @?= Success "b"
, testCase "fail if none of the parsers succeed" do
ensureFails (choice [string "a", string "b", string "bc"]) "c"
]
optionTests :: TestTree
optionTests = testGroup "option should"
[ testCase "succeed with Just if p succeeds" do
testParse (option (char 'a')) "a" @?= Success (Just 'a')
, testCase "succeed with Nothing if p fails withot consumption" do
testParse (option (char 'a')) "b" @?= Success Nothing
, testCase "fail if p fails with consumption" do
ensureFails (option (string "ab")) "a"
]
decideTests :: TestTree
decideTests = testGroup "decide should"
[ testCase "succeed for Just" do
testParse (decide (Just <$> char 'a')) "a" @?= Success 'a'
, testCase "fail for Nothing" do ensureFails @() (decide (pure Nothing)) ""
, testCase "compose with option to become identity" do
let id' = decide . option
(id' (pure 7) ~~ pure 7) [""]
(id' (char 'a') ~~ char 'a') ["", "a"]
(id' (string "ab") ~~ string "ab") ["", "a", "ab"]
]
fromMaybeSTests :: TestTree
fromMaybeSTests = testGroup "fromMaybeS should"
[ testCase "succeed for Just" do
testParse (fromMaybeS (pure 'b') (Just <$> char 'a')) "a" @?= Success 'a'
, testCase "succeed for None" do
testParse (fromMaybeS (pure 'b') (Nothing <$ char 'a')) "a" @?= Success 'b'
]
optionalTests :: TestTree
optionalTests = testGroup "optional should"
[ testCase "succeed if p succeeds" do
testParse (optional (char 'a')) "a" @?= Success ()
, testCase "also succeed if p fails without consumption" do
testParse (optional (char 'a')) "b" @?= Success ()
, testCase "fail if p failed with consumption" do
ensureFails (optional (string "ab")) "a"
]
manyNTests :: TestTree
manyNTests = testGroup "manyN should"
[ testCase "ensure that n are parsed" do
forM_ [0..10] \n -> do
testParse (manyN n item) (replicate n 'a') @?= Success (replicate n 'a')
ensureFails (manyN (n + 1) item) (replicate n 'a')
, testCase "not care if more are present" do
forM_ [0..10] \n ->
testParse (manyN n item) (replicate (n + 1) 'a') @?= Success (replicate (n + 1) 'a')
]
skipManyNTests :: TestTree
skipManyNTests = testGroup "skipManyN should"
[ testCase "ensure that n are parsed" do
forM_ [0..10] \n -> do
testParse (skipManyN n item) (replicate n 'a') @?= Success ()
ensureFails (skipManyN (n + 1) item) (replicate n 'a')
, testCase "not care if more are present" do
forM_ [0..10] \n ->
testParse (skipManyN n item) (replicate (n + 1) 'a') @?= Success ()
]
sepByTests :: TestTree
sepByTests = testGroup "sepBy should"
[ testCase "accept empty input" do
testParse (sepBy (char 'a') (char 'b')) "" @?= Success []
, testCase "parse more than 1" do
testParse (sepBy (char 'a') (char 'b')) "aba" @?= Success ['a', 'a']
]
sepBy1Tests :: TestTree
sepBy1Tests = testGroup "sepBy1 should"
[ testCase "not allow sep at the end of chain" do ensureFails p "ab"
, testCase "be able to testParse 2 or more p" do
testParse p "aba" @?= Success ['a', 'a']
testParse p "ababa" @?= Success ['a', 'a', 'a']
testParse p "abababa" @?= Success ['a', 'a', 'a', 'a']
, testCase "require a p" do
ensureFails p ""
testParse p "a" @?= Success ['a']
]
where p = sepBy1 (char 'a') (char 'b')
sepEndByTests :: TestTree
sepEndByTests = testGroup "sepEndBy should"
[ testCase "accept empty input" do
testParse (sepEndBy (char 'a') (char 'b')) "" @?= Success []
, testCase "parse more than 1" do
testParse (sepEndBy (char 'a') (char 'b')) "aba" @?= Success ['a', 'a']
]
sepEndBy1Tests :: TestTree
sepEndBy1Tests = testGroup "sepEndBy1 should"
[ testCase "require a p" do ensureFails p ""
, testCase "not require sep at end of chain" do testParse p "aa" @?= Success ["aa"]
, testCase "be able to testParse 2 or more p" do
testParse p "aabbaa" @?= Success ["aa", "aa"]
testParse p "aabbaabbaa" @?= Success ["aa", "aa", "aa"]
, testCase "be able to testParse a final sep" do
testParse p "aabb" @?= Success ["aa"]
testParse p "aabbaabb" @?= Success ["aa", "aa"]
testParse p "aabbaabbaabb" @?= Success ["aa", "aa", "aa"]
, testCase "fail if p fails after consuming input" do
ensureFails p "aabab"
, testCase "fail if sep fails after consuming input" do
ensureFails p "aab"
]
where p = sepEndBy1 (string "aa") (string "bb")
endByTests :: TestTree
endByTests = testGroup "endBy should"
[ testCase "accept empty input" do
testParse (endBy (char 'a') (char 'b')) "" @?= Success []
, testCase "parse more than 1" do
testParse (endBy (char 'a') (char 'b')) "abab" @?= Success ['a', 'a']
]
endBy1Tests :: TestTree
endBy1Tests = testGroup "endBy1 should"
[ testCase "require a p" do ensureFails p ""
, testCase "require a sep at the end of chain" do
ensureFails p "aa"
testParse p "aabb" @?= Success ["aa"]
, testCase "be able to testParse 2 or more p" do
testParse p "aabbaabb" @?= Success ["aa", "aa"]
testParse p "aabbaabbaabb" @?= Success ["aa", "aa", "aa"]
, testCase "fail if p fails after consuming input" do
ensureFails p "aaba"
]
where p = endBy1 (string "aa") (string "bb")
manyTillTests :: TestTree
manyTillTests = testGroup "manyTill should"
[ testCase "require an end" do
ensureFails p "aa"
testParse p "ab" @?= Success ['a']
, testCase "parse the end without result" do testParse p "b" @?= Success []
, testCase "parse p until the end is found" do
testParse p "aaaaaaaaaab" @?= Success (replicate 10 'a')
ensureFails (manyTill (string "aa") (char 'b')) "aaab"
]
where p = manyTill (char 'a') (char 'b')
someTillTests :: TestTree
someTillTests = testGroup "someTill should"
[ testCase "parse at least 1 p" do
testParse p "ab" @?= Success ['a']
ensureFails p "a"
ensureFails p "b"
]
where p = someTill (char 'a') (char 'b')
countTests :: TestTree
countTests = testGroup "count should"
[ testCase "report how many successful parses occurred" do
testParse p "" @?= Success 0
ensureFails q ""
testParse p "ab" @?= Success 1
testParse q "ab" @?= Success 1
testParse p "ababab" @?= Success 3
testParse q "ababab" @?= Success 3
, testCase "not allow partial results" do
ensureFails p "aba"
]
where p = count (string "ab")
q = count1 (string "ab")
exactlyTests :: TestTree
exactlyTests = testGroup "exactly should"
[ testCase "should be pure [] for n <= 0" do
(exactly 0 (char 'a') ~~ pure []) ["", "a"]
(exactly (-1) (char 'a') ~~ pure []) ["", "a"]
, testCase "should testParse n times for n > 0" do
forM_ [0..100] \n ->
testParse (exactly n (char 'a')) (replicate n 'a') @?= Success (replicate n 'a')
, testCase "fail if n inputs are not present" do
ensureFails (exactly 2 (char 'a')) "a"
]
rangeTests :: TestTree
rangeTests = testGroup "range should"
[ testCase "collect results up instead of count" do
ensureFails p "a"
testParse p "ab" @?= Success ['a', 'b']
testParse p "abc" @?= Success ['a', 'b', 'c']
testParse p "abcd" @?= Success ['a', 'b', 'c', 'd']
testParse p "abcde" @?= Success ['a', 'b', 'c', 'd', 'e']
testParse q "abcdef" @?= Success ['a', 'b', 'c', 'd', 'e']
, testCase "should act as pure [] when range is bad" do
(range (-1) 3 item ~~ pure []) ["", "a"]
(range 2 1 item ~~ pure []) ["", "a"]
]
where p = range 2 5 item <* eof
q = range 2 5 item <* char 'f'
range_Tests :: TestTree
range_Tests = testGroup "range_ should"
[ testCase "perform a range with no results" do
ensureFails p "a"
testParse p "ab" @?= Success ()
testParse p "abc" @?= Success ()
testParse p "abcd" @?= Success ()
testParse p "abcde" @?= Success ()
testParse q "abcdef" @?= Success ()
, testCase "should act as unit when range is bad" do
(range_ (-1) 3 item ~~ unit) ["", "a"]
(range_ 2 1 item ~~ unit) ["", "a"]
]
where p = range_ 2 5 item <* eof
q = range_ 2 5 item <* char 'f'
countRangeTests :: TestTree
countRangeTests = testGroup "countRange should"
[ testCase "count the parses within the range" do
ensureFails p "ab"
testParse p "abab" @?= Success 2
testParse p "ababab" @?= Success 3
testParse p "abababab" @?= Success 4
testParse p "ababababab" @?= Success 5
testParse p "abababababab" @?= Success 5
ensureFails p "ababababa"
ensureFails q "ab"
testParse q "abab" @?= Success 2
testParse q "ababab" @?= Success 3
testParse q "abababab" @?= Success 4
testParse q "ababababab" @?= Success 5
testParse q "abababababab" @?= Success 5
testParse q "ababababa" @?= Success 4
, testCase "should act as unit when range is bad" do
(countRange (-1) 3 item ~~ pure 0) ["", "a"]
(countRange 2 1 item ~~ pure 0) ["", "a"]
]
where p = countRange 2 5 (string "ab")
q = countRange 2 5 (atomic (string "ab"))