comparse-0.1.0.0: test/Parsing.hs
module Parsing where
import Control.Monad.Parser
import qualified Data.List.NonEmpty as NonEmpty
import Data.Stream.StringLines
import Test.Tasty
import Test.Tasty.HUnit
parse :: StringParser a -> String -> Maybe (a, String)
parse p s =
case runStringParser p s of
Parsed v (StringLines rest _) _ -> Just (v, rest)
_ -> Nothing
parserTests :: [TestTree]
parserTests =
[ coreParserTests
, coreCombinatorsTests
, derivedParserTests
, derivedCombinatorsTests
]
coreParserTests :: TestTree
coreParserTests =
testGroup
"Core parsers"
[ testCase "item returns next char" $
parse item "hi" @?= Just ('h', "i")
, testCase "item fails on eof" $
parse item "" @?= Nothing
, testCase "eof succeeds on eof" $
parse eof "" @?= Just ((), "")
, testCase "eof fails on non-eof" $
parse eof "hi" @?= Nothing
, testCase "noParse fails with non-empty input" $
parse (noParse :: StringParser ()) "hi" @?= Nothing
, testCase "noParse fails with empty input" $
parse (noParse :: StringParser ()) "" @?= Nothing
]
coreCombinatorsTests :: TestTree
coreCombinatorsTests =
testGroup
"Core combinators"
[ testCase "followedBy fails when parser fails" $
parse (followedBy $ like 'h') "ello" @?= Nothing
, testCase "followedBy succeeds without consuming input" $
parse (followedBy $ like 'h') "hi" @?= Just ((), "hi")
, testCase "notFollowedBy fails when parser succeeds" $
parse (notFollowedBy $ like 'h') "hi" @?= Nothing
, testCase "notFollowedBy succeeds without consuming input" $
parse (notFollowedBy $ like 'g') "hi" @?= Just ((), "hi")
, testCase "`a <|> b` returns a when it succeeds" $
parse (like 'h' <|> like 'g') "hi" @?= Just ('h', "i")
, testCase "`a <|> b` returns b when a fails" $
parse (like 'g' <|> like 'h') "hi" @?= Just ('h', "i")
, testCase "`a <|> b` fails when both fail" $
parse (like 'g' <|> like 'i') "hi" @?= Nothing
]
derivedParserTests :: TestTree
derivedParserTests =
testGroup
"Derived parsers"
[ testCase "like succeeds on matching item" $
parse (like 'h') "hi" @?= Just ('h', "i")
, testCase "like fails on non-matching item" $
parse (like 'h') "pi" @?= Nothing
, testCase "like fails on eof" $
parse (like 'h') "" @?= Nothing
, testCase "unlike succeeds on non-matching item" $
parse (unlike 'g') "hi" @?= Just ('h', "i")
, testCase "unlike fails on matching item" $
parse (unlike 'h') "hi" @?= Nothing
, testCase "string succeeds on matching string" $
parse (string "hell") "hello" @?= Just ("hell", "o")
, testCase "string fails on non-matching string" $
parse (string "hello") "hi" @?= Nothing
, testCase "`string \"\"` succeeds on arbitrary input" $
parse (string "") "hi" @?= Just ("", "hi")
, testCase "`string \"\"` succeeds on empty input" $
parse (string "") "" @?= Just ("", "")
]
derivedCombinatorsTests :: TestTree
derivedCombinatorsTests =
testGroup
"Derived combinators"
[ testCase "satisfy succeeds on matching char" $
parse (satisfy item (== 'h')) "hi" @?= Just ('h', "i")
, testCase "satisfy fails on non-matching char" $
parse (satisfy item (== 'g')) "hi" @?= Nothing
, testCase "optional returns Nothing on eof" $
parse (optional item) "" @?= Just (Nothing, "")
, testCase "optional returns Nothing on failure" $
parse (optional $ like 'g') "hi" @?= Just (Nothing, "hi")
, testCase "optional returns Just on success" $
parse (optional item) "hi" @?= Just (Just 'h', "i")
, testCase "many returns empty list on eof" $
parse (many item) "" @?= Just ([], "")
, testCase "many returns list of items" $
parse (many item) "hi" @?= Just ("hi", "")
, testCase "many1 fails on eof" $
parse (many1 item) "" @?= Nothing
, testCase "many1 returns non-empty list of items" $
parse (many1 item) "hi" @?= Just ("hi", "")
, testCase "some fails on eof" $
parse (some item) "" @?= Nothing
, testCase "some returns NonEmpty list of items" $
parse (some item) "hi" @?= Just (NonEmpty.fromList "hi", "")
, testCase "choice takes first successful parser" $
parse (choice [like 'h', like 'g']) "hi" @?= Just ('h', "i")
, testCase "choice with empty parser list fails" $
parse (choice [] :: StringParser ()) "hi" @?= Nothing
, testCase "choice with no matching parser fails" $
parse (choice [like 'g', like 'j']) "hi" @?= Nothing
, testCase "sepBy matches empty list" $
parse (sepBy item $ like ',') "" @?= Just ([], "")
, testCase "sepBy matches list of one item" $
parse (sepBy item $ like ',') "hi" @?= Just ("h", "i")
, testCase "sepBy matches list of many items" $
parse (sepBy item $ like ',') "h,e,llo" @?= Just ("hel", "lo")
, testCase "sepBy doesn't consume trailing separator" $
parse (sepBy item $ like ',') "h,i," @?= Just ("hi", ",")
, testCase "sepBy1 doesn't match empty list" $
parse (sepBy1 item $ like ',') "" @?= Nothing
, testCase "sepBy1 matches list of one item" $
parse (sepBy1 item $ like ',') "hi"
@?= Just (NonEmpty.fromList "h", "i")
, testCase "sepBy1 matches list of many items" $
parse (sepBy1 item $ like ',') "h,e,llo"
@?= Just (NonEmpty.fromList "hel", "lo")
, testCase "sepBy1 doesn't consume trailing separator" $
parse (sepBy1 item $ like ',') "h,i,"
@?= Just (NonEmpty.fromList "hi", ",")
, testCase "oneOf fails when item doesn't match any" $
parse (oneOf "gj") "hi" @?= Nothing
, testCase "oneOf succeeds when item matches one" $
parse (oneOf "gh") "hi" @?= Just ('h', "i")
, testCase "oneOf succeeds when item matches many" $
parse (oneOf "ghh") "hi" @?= Just ('h', "i")
, testCase "oneOf fails with empty class" $
parse (oneOf "") "hi" @?= Nothing
, testCase "noneOf succeeds when item doesn't match any" $
parse (noneOf "gj") "hi" @?= Just ('h', "i")
, testCase "noneOf fails when item matches one" $
parse (noneOf "gh") "hi" @?= Nothing
, testCase "noneOf fails when item matches many" $
parse (noneOf "ghh") "hi" @?= Nothing
, testCase "noneOf succeeds with empty class" $
parse (noneOf "") "hi" @?= Just ('h', "i")
]