{-# Language NoMonoLocalBinds #-} -- XXX We are using head/tail at one place #if __GLASGOW_HASKELL__ >= 908 {-# OPTIONS_GHC -Wno-x-partial #-} #endif module Streamly.Test.Data.Parser.Common (main) where import Control.Applicative ((<|>)) import Control.Exception (displayException, try, evaluate, SomeException) import Data.List (isSuffixOf) import Streamly.Internal.Data.MutByteArray (Unbox) import Streamly.Test.Common (listEquals, checkListEqual, chooseInt) import Streamly.Internal.Data.Parser (ParseErrorPos(..)) import Test.QuickCheck (arbitrary, forAll, elements, Property, property, listOf, vectorOf, Gen, (.&&.), ioProperty) import Test.QuickCheck.Monadic (monadicIO, assert, run, PropertyM) import Prelude hiding (sequence) import qualified Control.Monad.Fail as Fail import qualified Data.List as List import qualified Prelude import qualified Streamly.Internal.Data.Stream as S import qualified Streamly.Internal.Data.Array as A import qualified Streamly.Internal.Data.Array.Generic as GA import qualified Streamly.Internal.Data.Fold as FL import qualified Streamly.Internal.Data.Parser as P import qualified Streamly.Internal.Data.ParserK as PK import qualified Streamly.Internal.Data.StreamK as K import Test.Hspec import Test.Hspec.QuickCheck #if MIN_VERSION_QuickCheck(2,14,0) import Test.QuickCheck (chooseAny) import Control.Monad.Identity (Identity(runIdentity, Identity)) #else import System.Random (Random(random)) import Test.QuickCheck.Gen (Gen(MkGen)) -- | Generates a random element over the natural range of `a`. chooseAny :: Random a => Gen a chooseAny = MkGen (\r _ -> let (x,_) = random r in x) #endif min_value :: Int min_value = 0 mid_value :: Int mid_value = 5000 max_value :: Int max_value = 10000 max_length :: Int max_length = 1000 -- Accumulator Tests -- TODO: Replace ParserTestCase_Temp with ParserTestCase in all the test cases. type ParserTestCase a m b c = (P.Parser a m b -> [a] -> m (Either ParseErrorPos b, [a])) -> c type ParserTestCase_Temp a m b c = forall t. ([a] -> t) -> (P.Parser a m b -> t -> m (Either ParseErrorPos b)) -> c fromFold :: ParserTestCase Int IO Int Property fromFold consumer = forAll (listOf $ chooseInt (min_value, max_value)) $ \ls -> ioProperty $ do (s1, rest) <- consumer (P.fromFold FL.sum) ls o2 <- S.fold FL.sum (S.fromList ls) pure $ rest == [] && case s1 of Right o1 -> o1 == o2 Left _ -> False fromPure :: ParserTestCase Int Identity Int Property fromPure consumer = forAll (chooseInt (min_value, max_value)) $ \x -> case runIdentity $ consumer (P.fromPure x) [1 :: Int] of (Right r, rest) -> r == x && rest == [1 :: Int] (Left _, _) -> False fromEffect :: ParserTestCase Int Identity Int Property fromEffect consumer = forAll (chooseInt (min_value, max_value)) $ \x -> case runIdentity $ consumer (P.fromEffect $ return x) [1 :: Int] of (Right r, rest) -> r == x && rest == [1 :: Int] (Left _, _) -> False die :: ParserTestCase Int Identity Int Property die consumer = property $ case runIdentity $ consumer (P.die "die test") [0 :: Int] of (Right _, _) -> False (Left _, rest) -> rest == [0 :: Int] dieM :: ParserTestCase Int Identity Int Property dieM consumer = property $ case runIdentity $ consumer (P.dieM (Identity "die test")) [0 :: Int] of (Right _, _) -> False (Left _, rest) -> rest == [0 :: Int] parserFail :: ParserTestCase Int Identity Int Property parserFail consumer = property $ case runIdentity $ consumer (Fail.fail err) [0 :: Int] of (Right _, _) -> False (Left (ParseErrorPos _ e), rest) -> err == e && rest == [0 :: Int] where err = "Testing MonadFail.fail." -- Element Parser Tests peekPass :: ParserTestCase Int Identity Int Property peekPass consumer = forAll (chooseInt (1, max_length)) $ \list_length -> forAll (vectorOf list_length (chooseInt (min_value, max_value))) $ \ls -> case runIdentity $ consumer P.peek ls of (Right head_value, rest) -> case ls of head_ls : _ -> head_value == head_ls && rest == ls _ -> False (Left _, _) -> False peekFail :: ParserTestCase Int Identity Int Property peekFail consumer = property (case runIdentity $ consumer P.peek [] of (Right _, _) -> False (Left _, rest) -> rest == []) eofPass :: ParserTestCase_Temp Int Identity () Property eofPass producer consumer = property (case runIdentity $ consumer P.eof (producer []) of Right _ -> True Left _ -> False) eofFail :: ParserTestCase_Temp Int Identity () Property eofFail producer consumer = forAll (chooseInt (1, max_length)) $ \list_length -> forAll (vectorOf list_length (chooseInt (min_value, max_value))) $ \ls -> case runIdentity $ consumer P.eof (producer ls) of Right _ -> False Left _ -> True satisfyPass :: ParserTestCase_Temp Int Identity Int Property satisfyPass producer consumer = forAll (chooseInt (mid_value, max_value)) $ \first_element -> forAll (listOf (chooseInt (min_value, max_value))) $ \ls_tail -> let ls = first_element : ls_tail predicate = (>= mid_value) in case runIdentity $ consumer (P.satisfy predicate) (producer ls) of Right r -> r == first_element Left _ -> False satisfy :: ParserTestCase_Temp Int Identity Int Property satisfy producer consumer = forAll (listOf (chooseInt (min_value, max_value))) $ \ls -> case runIdentity $ consumer (P.satisfy predicate) (producer ls) of Right r -> case ls of [] -> False (x : _) -> predicate x && (r == x) Left _ -> case ls of [] -> True (x : _) -> not $ predicate x where predicate = (>= mid_value) onePass :: ParserTestCase_Temp Int Identity Int Property onePass producer consumer = forAll (chooseInt (1, max_value)) $ \int -> property (case runIdentity $ consumer P.one (producer [int]) of Right i -> i == int Left _ -> False) one :: ParserTestCase_Temp Int Identity Int Property one producer consumer = property $ case runIdentity $ consumer P.one (producer []) of Left _ -> True Right _ -> False -- Sequence Parsers Tests takeBetweenPass :: ParserTestCase_Temp Int Identity [Int] Property takeBetweenPass producer consumer = forAll (chooseInt (min_value, max_value)) $ \m -> forAll (chooseInt (m, max_value)) $ \n -> forAll (chooseInt (m, max_value)) $ \list_length -> forAll (vectorOf list_length (chooseInt (min_value, max_value))) $ \ls -> case runIdentity $ consumer (P.takeBetween m n FL.toList) (producer ls) of Right parsed_list -> let lpl = Prelude.length parsed_list in checkListEqual parsed_list $ Prelude.take lpl ls Left _ -> property False takeBetween :: ParserTestCase_Temp Int Identity [Int] Property takeBetween producer consumer = forAll (chooseInt (min_value, max_value)) $ \m -> forAll (chooseInt (min_value, max_value)) $ \n -> forAll (listOf (chooseInt (min_value, max_value))) $ \ls -> ioProperty $ go m n ls where go m n ls = do let inputLen = Prelude.length ls let p = P.takeBetween m n FL.toList eres <- try $ evaluate $ runIdentity $ consumer p (producer ls) pure $ case eres of Left (_ :: SomeException) -> m >= 0 && n >= 0 && m > n Right (Right xs) -> let parsedLen = Prelude.length xs in (inputLen >= m && parsedLen >= m && parsedLen <= n) && (xs == Prelude.take parsedLen ls) Right (Left _) -> inputLen < m take :: ParserTestCase_Temp Int Identity [Int] Property take producer consumer = forAll (chooseInt (min_value, max_value)) $ \n -> forAll (listOf (chooseInt (min_value, max_value))) $ \ls -> case runIdentity $ consumer (P.fromFold $ FL.take n FL.toList) (producer ls) of Right parsed_list -> checkListEqual parsed_list (Prelude.take n ls) Left _ -> property False takeEQPass :: ParserTestCase_Temp Int Identity [Int] Property takeEQPass producer consumer = forAll (chooseInt (min_value, max_value)) $ \n -> forAll (chooseInt (n, max_value)) $ \list_length -> forAll (vectorOf list_length (chooseInt (min_value, max_value))) $ \ls -> case runIdentity $ consumer (P.takeEQ n FL.toList) (producer ls) of Right parsed_list -> checkListEqual parsed_list (Prelude.take n ls) Left _ -> property False takeEQ :: ParserTestCase_Temp Int Identity [Int] Property takeEQ producer consumer = forAll (chooseInt (min_value, max_value)) $ \n -> forAll (listOf (chooseInt (min_value, max_value))) $ \ls -> let list_length = Prelude.length ls in case runIdentity $ consumer (P.takeEQ n FL.toList) (producer ls) of Right parsed_list -> if n <= list_length then checkListEqual parsed_list (Prelude.take n ls) else property False Left _ -> property (n > list_length) takeGEPass :: ParserTestCase_Temp Int Identity [Int] Property takeGEPass producer consumer = forAll (chooseInt (min_value, max_value)) $ \n -> forAll (chooseInt (n, max_value)) $ \list_length -> forAll (vectorOf list_length (chooseInt (min_value, max_value))) $ \ls -> case runIdentity $ consumer (P.takeGE n FL.toList) (producer ls) of Right parsed_list -> checkListEqual parsed_list ls Left _ -> property False takeGE :: ParserTestCase_Temp Int Identity [Int] Property takeGE producer consumer = forAll (chooseInt (min_value, max_value)) $ \n -> forAll (listOf (chooseInt (min_value, max_value))) $ \ls -> let list_length = Prelude.length ls in case runIdentity $ consumer (P.takeGE n FL.toList) (producer ls) of Right parsed_list -> if n <= list_length then checkListEqual parsed_list ls else property False Left _ -> property (n > list_length) nLessThanEqual0 :: ( Int -> FL.Fold Identity Int [Int] -> P.Parser Int Identity [Int] ) -> (Int -> [Int] -> [Int]) -> ParserTestCase_Temp Int Identity [Int] Property nLessThanEqual0 tk ltk producer consumer = forAll (elements [0, (-1)]) $ \n -> forAll (listOf arbitrary) $ \ls -> case runIdentity $ consumer (tk n FL.toList) (producer ls) of Right parsed_list -> checkListEqual parsed_list (ltk n ls) Left _ -> property False takeProperties :: ParserTestCase_Temp Int Identity [Int] Spec takeProperties producer consumer = describe "take combinators when n <= 0/" $ do prop "takeEQ n FL.toList = []" $ nLessThanEqual0 P.takeEQ (\_ -> const []) producer consumer prop "takeGE n FL.toList xs = xs" $ nLessThanEqual0 P.takeGE (\_ -> id) producer consumer -- XXX lookAhead can't deal with EOF which in this case means when -- n==list_length, this test will fail. So excluding that case for now. lookAheadPass :: ParserTestCase_Temp Int Identity ([Int], [Int]) Property lookAheadPass producer consumer = forAll (chooseInt (min_value, max_value)) $ \n -> let takeWithoutConsume = P.lookAhead $ P.fromFold $ FL.take n FL.toList parseTwice = do parsed_list_1 <- takeWithoutConsume parsed_list_2 <- takeWithoutConsume return (parsed_list_1, parsed_list_2) in forAll (chooseInt (n+1, max_value)) $ \list_length -> forAll (vectorOf list_length (chooseInt (min_value, max_value))) $ \ls -> case runIdentity $ consumer parseTwice (producer ls) of Right (ls_1, ls_2) -> checkListEqual ls_1 ls_2 .&&. checkListEqual ls_1 (Prelude.take n ls) Left _ -> property $ False -- lookAheadFail :: ParserTestCase_Temp Int Identity Int Property -- lookAheadFail producer consumer = -- forAll (chooseInt (min_value + 1, max_value)) $ \n -> -- let -- takeWithoutConsume = P.lookAhead $ P.take n FL.toList -- parseTwice = do -- parsed_list_1 <- takeWithoutConsume -- parsed_list_2 <- takeWithoutConsume -- return (parsed_list_1, parsed_list_2) -- in -- forAll (chooseInt (min_value, n - 1)) $ \list_length -> -- forAll (vectorOf list_length (chooseInt (min_value, max_value))) $ \ls -> -- case consumer parseTwice (producer ls) of -- Right _ -> False -- Left _ -> True lookAhead :: ParserTestCase_Temp Int Identity ([Int], [Int]) Property lookAhead producer consumer = forAll (chooseInt (min_value, max_value)) $ \n -> let takeWithoutConsume = P.lookAhead $ P.fromFold $ FL.take n FL.toList parseTwice = do parsed_list_1 <- takeWithoutConsume parsed_list_2 <- takeWithoutConsume return (parsed_list_1, parsed_list_2) in forAll (listOf (chooseInt (min_value, max_value))) $ \ls -> case runIdentity $ consumer parseTwice (producer ls) of Right (ls_1, ls_2) -> checkListEqual ls_1 ls_2 .&&. checkListEqual ls_1 (Prelude.take n ls) Left _ -> property ((list_length < n) || (list_length == n && n == 0)) where list_length = Prelude.length ls listTakeEndBy_ :: (a -> Bool) -> [a] -> ([a], [a]) listTakeEndBy_ predicate ls = case break predicate ls of (a, []) -> (a, []) (a, b) -> (a, tail b) takeEndBy_ :: ParserTestCase Int Identity [Int] Property takeEndBy_ consumer = forAll (listOf (chooseInt (min_value, max_value ))) $ \ls -> case runIdentity $ consumer (P.takeEndBy_ predicate prsr) ls of (Right parsed_list, rest) -> monadicIO $ do let (ls1, ls2) = listTakeEndBy_ predicate ls listEquals (==) parsed_list ls1 listEquals (==) rest ls2 _ -> property False where predicate = (>= 100) prsr = P.many (P.satisfy (const True)) FL.toList takeEndByOrMax_ :: ParserTestCase Int Identity [Int] Property takeEndByOrMax_ consumer = forAll (chooseInt (min_value, max_value)) $ \n -> forAll (listOf (chooseInt (0, 1))) $ \ls -> case runIdentity $ consumer (P.fromFold $ FL.takeEndBy_ predicate (FL.take n FL.toList)) ls of (Right parsed_list, rest) -> monadicIO $ do let (lsa, lsb) = listTakeEndBy_ predicate ls (ls1, ls2) = Prelude.splitAt n ls if length lsa < length ls1 then do listEquals (==) parsed_list lsa listEquals (==) rest lsb else do listEquals (==) parsed_list ls1 listEquals (==) rest ls2 _ -> property False where predicate = (== 1) takeStartBy :: ParserTestCase Int Identity [Int] Property takeStartBy consumer = forAll (listOf (chooseInt (min_value, max_value))) $ \ls -> let ls1 = 1:ls in case runIdentity $ consumer parser ls1 of (Right parsed_list, rest) -> if not $ Prelude.null ls1 then let (rls1, rls2) = Prelude.break predicate (tail ls1) in monadicIO $ do listEquals (==) parsed_list (head ls1 : rls1) listEquals (==) rest rls2 else property $ Prelude.null parsed_list && Prelude.null rest _ -> property False where predicate = odd parser = P.takeBeginBy predicate FL.toList takeWhile :: ParserTestCase Int Identity [Int] Property takeWhile consumer = forAll (listOf (chooseInt (0, 1))) $ \ ls -> case runIdentity $ consumer (P.takeWhile predicate FL.toList) ls of (Right parsed_list, rest) -> monadicIO $ do let (ls1, ls2) = Prelude.span predicate ls listEquals (==) parsed_list ls1 listEquals (==) rest ls2 _ -> property False where predicate = (== 0) takeP :: ParserTestCase Int Identity [Int] Property takeP consumer = forAll ((,) <$> chooseInt (min_value, max_value) <*> listOf (chooseInt (0, 1))) $ \(takeNum, ls) -> case runIdentity $ consumer (P.takeP takeNum (P.fromFold FL.toList)) ls of (Right parsed_list, rest) -> monadicIO $ do let (ls1, ls2) = Prelude.splitAt takeNum ls listEquals (==) parsed_list ls1 listEquals (==) rest ls2 _ -> property False takeWhile1 :: ParserTestCase Int Identity [Int] Property takeWhile1 consumer = forAll (listOf (chooseInt (0, 1))) $ \ ls -> case runIdentity $ consumer (P.takeWhile1 predicate FL.toList) ls of (Right parsed_list, rest) -> case ls of [] -> property False (x : _) -> if predicate x then monadicIO $ do let (ls1, ls2) = span predicate ls listEquals (==) parsed_list ls1 listEquals (==) rest ls2 else property False (Left _, rest) -> monadicIO $ do listEquals (==) rest ls case ls of [] -> assert True (x : _) -> assert (not $ predicate x) where predicate = (== 0) takeWhileP :: ParserTestCase Int Identity [Int] Property takeWhileP consumer = forAll (listOf (chooseInt (0, 1))) $ \ls -> forAll (chooseInt (min_value, max_value)) $ \n -> let predicate = (== 1) prsr = P.takeWhileP predicate $ P.fromFold (FL.take n FL.toList) takeWhileTillLen maxLen prd list = Prelude.take maxLen $ Prelude.takeWhile prd list in case runIdentity $ consumer prsr ls of (Right parsed_list, rest) -> monadicIO $ do let ls1 = takeWhileTillLen n predicate ls ls2 = drop (length ls1) ls listEquals (==) parsed_list ls1 listEquals (==) rest ls2 _ -> property False {- choice :: ParserTestCase_Temp Int Identity Int Property choice producer consumer = forAll ((,,) <$> chooseInt (min_value, max_value) <*> chooseInt (min_value, max_value) <*> listOf (chooseInt (0, 1))) $ \(i, j, ls) -> case consumer (P.choice [parser i, parser j]) (producer ls) of Right parsed_list -> checkListEqual parsed_list $ take (min i j) ls Left _ -> property False where parser i = P.fromFold (FL.take i FL.toList) -} groupBy :: ParserTestCase_Temp Int Identity [Int] Property groupBy producer consumer = forAll (listOf (chooseInt (0, 1))) $ \ls -> case runIdentity $ consumer parser (producer ls) of Right parsed -> checkListEqual parsed (groupByLF ls) Left _ -> property False where cmp = (==) parser = P.groupBy cmp FL.toList groupByLF lst | null lst = [] | otherwise = head $ List.groupBy cmp lst groupByRolling :: ParserTestCase_Temp Int Identity [Int] Property groupByRolling producer consumer = forAll (listOf (chooseInt (0, 1))) $ \ls -> case runIdentity $ consumer parser (producer ls) of Right parsed -> checkListEqual parsed (groupByLF Nothing ls) Left _ -> property False where cmp = (==) parser = P.groupBy cmp FL.toList groupByLF _ [] = [] groupByLF Nothing (x:xs) = x : groupByLF (Just x) xs groupByLF (Just y) (x:xs) = if cmp y x then x : groupByLF (Just x) xs else [] wordBy :: ParserTestCase_Temp Char Identity [String] Property wordBy producer consumer = forAll (listOf (elements [' ', 's'])) $ \ls -> case runIdentity $ consumer parser (producer ls) of Right parsed -> checkListEqual parsed (words' ls) Left _ -> property False where predicate = (== ' ') parser = P.many (P.wordBy predicate FL.toList) FL.toList words' lst = let wrds = words lst in if wrds == [] && length lst > 0 then [""] else wrds splitWith :: ParserTestCase_Temp Int Identity (Int, Int) Property splitWith producer consumer = forAll (listOf (chooseInt (0, 1))) $ \ls -> case runIdentity $ consumer (P.splitWith (,) (P.satisfy (== 0)) (P.satisfy (== 1))) (producer ls) of Right (result_first, result_second) -> case ls of 0 : 1 : _ -> (result_first == 0) && (result_second == 1) _ -> False Left _ -> case ls of 0 : 1 : _ -> False _ -> True splitWithFailLeft :: ParserTestCase_Temp Int Identity (Int, Int) Property splitWithFailLeft producer consumer = property (case runIdentity $ consumer (P.splitWith (,) (P.die "die") (P.fromPure (1 :: Int))) (producer [1 :: Int]) of Right _ -> False Left _ -> True) splitWithFailRight :: ParserTestCase_Temp Int Identity (Int, Int) Property splitWithFailRight producer consumer = property (case runIdentity $ consumer (P.splitWith (,) (P.fromPure (1 :: Int)) (P.die "die")) (producer [1 :: Int]) of Right _ -> False Left _ -> True) splitWithFailBoth :: ParserTestCase_Temp Int Identity (Int, Int) Property splitWithFailBoth producer consumer = property (case runIdentity $ consumer (P.splitWith (,) (P.die "die") (P.die "die")) (producer [1 :: Int]) of Right _ -> False Left _ -> True) -- teeWithPass :: ParserTestCase_Temp Int Identity Int Property -- teeWithPass producer consumer = -- forAll (chooseInt (min_value, max_value)) $ \n -> -- forAll (listOf (chooseInt (0, 1))) $ \ls -> -- let -- prsr = P.fromFold $ FL.take n FL.toList -- in -- case consumer (P.teeWith (,) prsr prsr) (producer ls) of -- Right (ls_1, ls_2) -> checkListEqual (Prelude.take n ls) ls_1 .&&. checkListEqual ls_1 ls_2 -- Left _ -> property False -- teeWithFailLeft :: ParserTestCase_Temp Int Identity Int Property -- teeWithFailLeft producer consumer = -- property (case consumer (P.teeWith (,) (P.die "die") (P.fromPure (1 :: Int))) (producer [1 :: Int]) of -- Right _ -> False -- Left _ -> True) -- teeWithFailRight :: ParserTestCase_Temp Int Identity Int Property -- teeWithFailRight producer consumer = -- property (case consumer (P.teeWith (,) (P.fromPure (1 :: Int)) (P.die "die")) (producer [1 :: Int]) of -- Right _ -> False -- Left _ -> True) -- teeWithFailBoth :: ParserTestCase_Temp Int Identity Int Property -- teeWithFailBoth producer consumer = -- property (case consumer (P.teeWith (,) (P.die "die") (P.die "die")) (producer [1 :: Int]) of -- Right _ -> False -- Left _ -> True) {- deintercalate :: ParserTestCase_Temp Int Identity Int Property deintercalate producer consumer = forAll (listOf (chooseAny :: Gen Int)) $ \ls -> case runIdentity $ consumer p (producer ls) of Right evenOdd -> evenOdd == List.partition even ls Left _ -> False where p1 = P.takeWhile even FL.toList p2 = P.takeWhile odd FL.toList partition = FL.tee (fmap concat $ FL.catLefts FL.toList) (fmap concat $ FL.catRights FL.toList) p = P.deintercalate p1 p2 partition -} -- shortestPass :: ParserTestCase_Temp Int Identity Int Property -- shortestPass producer consumer = -- forAll (listOf (chooseInt(min_value, max_value))) $ \ls -> -- let -- half_mid_value = mid_value `Prelude.div` 2 -- prsr_1 = P.takeWhile (<= half_mid_value) FL.toList -- prsr_2 = P.takeWhile (<= mid_value) FL.toList -- prsr_shortest = P.shortest prsr_1 prsr_2 -- in -- case consumer prsr_shortest (producer ls) of -- Right short_list -> checkListEqual short_list (Prelude.takeWhile (<= half_mid_value) ls) -- Left _ -> property False -- shortestPassLeft :: ParserTestCase_Temp Int Identity Int Property -- shortestPassLeft producer consumer = -- property (case consumer (P.shortest (P.die "die") (P.fromPure (1 :: Int))) (producer [1 :: Int]) of -- Right r -> r == 1 -- Left _ -> False) -- -- shortestPassRight :: ParserTestCase_Temp Int Identity Int Property -- shortestPassRight producer consumer = -- property (case consumer (P.shortest (P.fromPure (1 :: Int)) (P.die "die")) (producer [1 :: Int]) of -- Right r -> r == 1 -- Left _ -> False) -- shortestFailBoth :: ParserTestCase_Temp Int Identity Int Property -- shortestFailBoth producer consumer = -- property -- (case consumer -- (P.shortest (P.die "die") (P.die "die")) -- (producer [1 :: Int]) of -- Right _ -> False -- Left _ -> True) -- -- longestPass :: ParserTestCase_Temp Int Identity Int Property -- longestPass producer consumer = -- forAll (listOf (chooseInt(min_value, max_value))) $ \ls -> -- let -- half_mid_value = mid_value `Prelude.div` 2 -- prsr_1 = P.takeWhile (<= half_mid_value) FL.toList -- prsr_2 = P.takeWhile (<= mid_value) FL.toList -- prsr_longest = P.longest prsr_1 prsr_2 -- in -- case consumer prsr_longest (producer ls) of -- Right long_list -> long_list == Prelude.takeWhile (<= mid_value) ls -- Left _ -> False -- -- longestPassLeft :: ParserTestCase_Temp Int Identity Int Property -- longestPassLeft producer consumer = -- property (case consumer (P.shortest (P.die "die") (P.fromPure (1 :: Int))) (producer [1 :: Int]) of -- Right r -> r == 1 -- Left _ -> False) -- -- longestPassRight :: ParserTestCase_Temp Int Identity Int Property -- longestPassRight producer consumer = -- property (case consumer (P.shortest (P.fromPure (1 :: Int)) (P.die "die")) (producer [1 :: Int]) of -- Right r -> r == 1 -- Left _ -> False) -- -- longestFailBoth :: ParserTestCase_Temp Int Identity Int Property -- longestFailBoth producer consumer = -- property -- (case consumer (P.shortest (P.die "die") (P.die "die")) (producer [1 :: Int]) of -- Right _ -> False -- Left _ -> True) many :: ParserTestCase_Temp Int Identity [Int] Property many producer consumer = forAll (listOf (chooseInt (0, 1))) $ \ls -> let fldstp conL currL = return $ FL.Partial (conL ++ currL) concatFold = FL.Fold fldstp (return (FL.Partial [])) return return prsr = flip P.many concatFold $ P.fromFold $ FL.takeEndBy_ (== 1) FL.toList in case runIdentity $ consumer prsr (producer ls) of Right res_list -> checkListEqual res_list (Prelude.filter (== 0) ls) Left _ -> property False many_empty :: ParserTestCase_Temp Int Identity [Int] Property many_empty producer consumer = property (case runIdentity $ consumer (flip P.many FL.toList (P.die "die")) (producer [1 :: Int]) of Right res_list -> checkListEqual res_list ([] :: [Int]) Left _ -> property False) some :: ParserTestCase_Temp Int Identity [Int] Property some producer consumer = forAll (listOf (chooseInt (0, 1))) $ \genLs -> let ls = 0 : genLs fldstp conL currL = return $ FL.Partial $ conL ++ currL concatFold = FL.Fold fldstp (return (FL.Partial [])) return return prsr = flip P.some concatFold $ P.fromFold $ FL.takeEndBy_ (== 1) FL.toList in case runIdentity $ consumer prsr (producer ls) of Right res_list -> res_list == Prelude.filter (== 0) ls Left _ -> False someFail :: ParserTestCase_Temp Int Identity [Int] Property someFail producer consumer = property (case runIdentity $ consumer (P.some (P.die "die") FL.toList) (producer [1 :: Int]) of Right _ -> False Left _ -> True) ------------------------------------------------------------------------------- -- Instances ------------------------------------------------------------------------------- applicative :: ParserTestCase_Temp Int Identity ([Int], [Int]) Property applicative producer consumer = forAll (listOf (chooseAny :: Gen Int)) $ \ list1 -> forAll (listOf (chooseAny :: Gen Int)) $ \ list2 -> let parser = (,) <$> P.fromFold (FL.take (length list1) FL.toList) <*> P.fromFold (FL.take (length list2) FL.toList) in case runIdentity $ consumer parser (producer $ list1 ++ list2) of Right (olist1, olist2) -> olist1 == list1 && olist2 == list2 Left _ -> False sequence :: ParserTestCase_Temp Int IO [[Int]] Property sequence producer consumer = forAll (vectorOf 11 (listOf (chooseAny :: Gen Int))) $ \ ins -> let p xs = P.fromFold (FL.take (length xs) FL.toList) in monadicIO $ do outs <- run $ consumer (Prelude.sequence $ fmap p ins) (producer $ concat ins) return $ case outs of Right ls -> ls == ins Left _ -> False altEOF1 :: ParserTestCase_Temp Int (PropertyM IO) Int Property altEOF1 producer consumer = monadicIO $ do s1 <- consumer (P.satisfy (> 0) <|> return 66) (producer ([]::[Int])) return $ case s1 of Right x -> x == 66 Left _ -> False altEOF2 :: ParserTestCase_Temp Int (PropertyM IO) [Int] Property altEOF2 producer consumer = monadicIO $ do s1 <- consumer ((P.takeEQ 2 FL.toList) <|> (P.takeEQ 1 FL.toList)) (producer ([51]::[Int])) return $ case s1 of Right x -> x == [51] Left _ -> False monad :: ParserTestCase_Temp Int (PropertyM IO) ([Int], [Int]) Property monad producer consumer = forAll (listOf (chooseAny :: Gen Int)) $ \ list1 -> forAll (listOf (chooseAny :: Gen Int)) $ \ list2 -> let parser = do olist1 <- P.fromFold (FL.take (length list1) FL.toList) olist2 <- P.fromFold (FL.take (length list2) FL.toList) return (olist1, olist2) in monadicIO $ do s <- consumer parser (producer $ list1 ++ list2) return $ case s of Right (olist1, olist2) -> olist1 == list1 && olist2 == list2 Left _ -> False takeEndBy1 :: ParserTestCase_Temp Int Identity [Int] Property takeEndBy1 producer consumer = forAll (listOf (chooseInt (0, 1))) $ \ls -> case runIdentity $ consumer (P.takeEndBy predicate prsr) (producer ls) of Right parsed_list -> checkListEqual parsed_list (takeWhileAndFirstFail (not . predicate) ls) Left _ -> property False where prsr = P.many (P.satisfy (const True)) FL.toList predicate = (== 1) takeWhileAndFirstFail prd (x : xs) = if prd x then x : takeWhileAndFirstFail prd xs else [x] takeWhileAndFirstFail _ [] = [] takeEndByEsc :: ParserTestCase_Temp Int Identity [Int] Property takeEndByEsc producer consumer = forAll (listOf (chooseInt (min_value, max_value))) $ \ls -> let msg = "takeEndByEsc: trailing escape" isSep = even isEsc x = x `mod` 6 == 0 prsr = P.takeEndByEsc isEsc isSep prsr0 where prsr0 = P.many (P.satisfy (const True)) FL.toList escapeSep maybePrevEsc [] = case maybePrevEsc of Nothing -> [] Just prevEsc -> [prevEsc] escapeSep maybePrevEsc (x : xs) = case maybePrevEsc of Nothing -> if isEsc x then escapeSep (Just x) xs else if isSep x then [x] else x : escapeSep Nothing xs Just _ -> x : escapeSep Nothing xs in case runIdentity $ consumer prsr (producer ls) of Right parsed_list -> checkListEqual parsed_list $ escapeSep Nothing ls Left err -> property (msg `isSuffixOf` displayException err) takeFramedByEsc_ :: ParserTestCase_Temp Int Identity [Int] Property takeFramedByEsc_ producer consumer = forAll (listOf (chooseInt (min_value, max_value))) $ \ls -> let isBegin = (== 0) isEnd = (== 1) isEsc = (== 2) prsr = P.takeFramedByEsc_ isEsc isBegin isEnd FL.toList checkPass (x : xs) maybePrevEsc openMinusClose = case maybePrevEsc of Nothing -> if isEsc x then checkPass xs (Just x) openMinusClose else if isBegin x then checkPass xs Nothing (openMinusClose + 1) else if isEnd x then case openMinusClose of 0 -> False 1 -> True _ -> checkPass xs Nothing (openMinusClose - 1) else checkPass xs Nothing openMinusClose Just _ -> checkPass xs Nothing openMinusClose checkPass [] _ _ = False checkPassBeg [] = False checkPassBeg xxs@(x:_) | isBegin x = checkPass xxs Nothing (0 :: Int) | otherwise = False escapeFrame begin end escape l = let helper (x : xs) maybePrevEsc openMinusClose = case maybePrevEsc of Nothing -> if escape x then helper xs (Just x) openMinusClose else if begin x then helper xs Nothing (openMinusClose + 1) else if end x then if openMinusClose - 1 == 0 then [] else helper xs Nothing (openMinusClose - 1) else x : helper xs Nothing openMinusClose Just prevEsc -> if escape x || begin x || end x then x : helper xs Nothing openMinusClose else prevEsc : x : helper xs Nothing openMinusClose helper [] _ _ = error "Cannot Reach Here" in helper l Nothing (0 :: Int) in case runIdentity $ consumer prsr (producer ls) of Right parsed_list -> if checkPassBeg ls then checkListEqual parsed_list $ escapeFrame isBegin isEnd isEsc ls else property False Left _ -> if checkPassBeg ls then property False else property True takeFramedByEsc_Pass :: ParserTestCase_Temp Int Identity [Int] Property takeFramedByEsc_Pass producer consumer = forAll (listOf (chooseInt (min_value, max_value))) $ \list -> let ls = (0 : list) ++ (Prelude.replicate (Prelude.length list + 1) 1) isBegin = (== 0) isEnd = (== 1) isEsc = (== 2) prsr = P.takeFramedByEsc_ isEsc isBegin isEnd FL.toList escapeFrame begin end escape l = let helper (x : xs) maybePrevEsc openMinusClose = case maybePrevEsc of Nothing -> if escape x then helper xs (Just x) openMinusClose else if begin x then if openMinusClose == 0 then helper xs Nothing (openMinusClose + 1) else x : helper xs Nothing (openMinusClose + 1) else if end x then if openMinusClose - 1 == 0 then [] else x : helper xs Nothing (openMinusClose - 1) else x : helper xs Nothing openMinusClose Just _ -> x : helper xs Nothing openMinusClose helper [] _ _ = error "Cannot Reach Here" in helper l Nothing (0 :: Int) in case runIdentity $ consumer prsr (producer ls) of Right parsed_list -> checkListEqual parsed_list $ escapeFrame isBegin isEnd isEsc ls _ -> property False takeFramedByEsc_Fail1 :: ParserTestCase_Temp Int Identity [Int] Property takeFramedByEsc_Fail1 producer consumer = let msg = "takeFramedByEsc_: missing frame end" isBegin = (== 0) isEnd = (== 0) isEsc = (== 2) prsr = P.takeFramedByEsc_ isEsc isBegin isEnd FL.toList ls = [0 :: Int] in case runIdentity $ consumer prsr (producer ls) of Right _ -> property False Left err -> property (msg `isSuffixOf` displayException err) takeFramedByEsc_Fail2 :: ParserTestCase_Temp Int Identity [Int] Property takeFramedByEsc_Fail2 producer consumer = let msg = "takeFramedByEsc_: missing frame start" isBegin = (== 0) isEnd = (== 1) isEsc = (== 1) prsr = P.takeFramedByEsc_ isEsc isBegin isEnd FL.toList ls = [1 :: Int] in case runIdentity $ consumer prsr (producer ls) of Right _ -> property False Left err -> property (msg `isSuffixOf` displayException err) takeFramedByEsc_Fail3 :: ParserTestCase_Temp Int Identity [Int] Property takeFramedByEsc_Fail3 producer consumer = let msg = "takeFramedByEsc_: missing frame end" isBegin = (== 2) isEnd = (== 1) isEsc = (== 2) prsr = P.takeFramedByEsc_ isEsc isBegin isEnd FL.toList ls = [2 :: Int] in case runIdentity $ consumer prsr (producer ls) of Right _ -> property False Left err -> property (msg `isSuffixOf` displayException err) takeStartBy_ :: ParserTestCase_Temp Int Identity [Int] Property takeStartBy_ producer consumer = forAll (listOf (chooseInt (min_value, max_value))) $ \ls -> let ls1 = 1:ls msg = "takeFramedByGeneric: empty token" in case runIdentity $ consumer parser (producer ls1) of Right parsed_list -> if not $ Prelude.null ls1 then let tls = Prelude.takeWhile (not . predicate) (tail ls1) in checkListEqual parsed_list $ if predicate (head ls1) then tls else Prelude.takeWhile (not . predicate) ls1 else property $ Prelude.null parsed_list Left err -> property (msg `isSuffixOf` displayException err) where predicate = odd parser = P.takeBeginBy_ predicate FL.toList ------------------------------------------------------------------------------- -- Main ------------------------------------------------------------------------------- data TestMode = TMParserStream | TMParserKStreamK | TMParserKStreamKChunks | TMParserKStreamKChunksGeneric deriving (Show) runParserTC :: (Unbox a, Monad m) => TestMode -> ParserTestCase a m b c -> c runParserTC tm runner = case tm of TMParserStream -> runner $ \p -> mapMTup S.toList . S.parseBreakPos p . S.fromList TMParserKStreamK -> runner $ \p -> mapMTup K.toList . K.parseBreakPos (PK.toParserK p) . K.fromList TMParserKStreamKChunks -> runner $ \p -> mapMTup (fmap (concatMap A.toList) . K.toList) . A.parseBreakPos (A.toParserK p) . producerChunks A.fromList TMParserKStreamKChunksGeneric -> runner $ \p -> mapMTup (fmap (concatMap GA.toList) . K.toList) . GA.parseBreakPos (GA.toParserK p) . producerChunks GA.fromList where mapMTup f tupM = do (t, a) <- tupM (t,) <$> f a cSize = 50 -- Not using A.createOf here because of the MonadIO constraint producerChunks fl = K.fromStream . S.groupsOf cSize (fl <$> FL.toList) . S.fromList runParserTC_temp :: (Unbox a, Monad m) => TestMode -> ParserTestCase_Temp a m b c -> c runParserTC_temp tm runner = case tm of TMParserStream -> runner S.fromList S.parsePos TMParserKStreamK -> runner K.fromList (K.parsePos . PK.toParserK) TMParserKStreamKChunks -> runner (producerChunks A.fromList) (A.parsePos . A.toParserK) TMParserKStreamKChunksGeneric -> runner (producerChunks GA.fromList) (GA.parsePos . GA.toParserK) where cSize = 50 -- Not using A.createOf here because of the MonadIO constraint producerChunks fl = K.fromStream . S.groupsOf cSize (fl <$> FL.toList) . S.fromList {-# NOINLINE mainCommon #-} mainCommon :: TestMode -> Spec mainCommon ptt = do describe (show ptt) $ do describe "Instances" $ do prop "applicative" $ runParserTC_temp ptt applicative prop "Alternative: end of input 1" $ runParserTC_temp ptt altEOF1 prop "Alternative: end of input 2" $ runParserTC_temp ptt altEOF2 prop "monad" $ runParserTC_temp ptt monad prop "sequence" $ runParserTC_temp ptt sequence describe "test for accumulator" $ do prop "P.fromFold FL.sum = FL.sum" $ runParserTC ptt fromFold prop "fromPure value provided" $ runParserTC ptt fromPure prop "fromPure monadic value provided" $ runParserTC ptt fromEffect prop "fail err = Left (SomeException (ParseError err))" $ runParserTC ptt parserFail prop "always fail" $ runParserTC ptt die prop "always fail but monadic" $ runParserTC ptt dieM describe "test for element parser" $ do prop "peek = head with list length > 0" $ runParserTC ptt peekPass prop "peek fail on []" $ runParserTC ptt peekFail prop "eof pass on []" $ runParserTC_temp ptt eofPass prop "eof fail on non-empty list" $ runParserTC_temp ptt eofFail prop "first element exists and >= mid_value" $ runParserTC_temp ptt satisfyPass prop "one pass on [Int]" $ runParserTC_temp ptt onePass prop "one fail on []" $ runParserTC_temp ptt one prop "check first element exists and satisfies predicate" $ runParserTC_temp ptt satisfy describe "test for sequence parser" $ do prop "P.takeBetween = Prelude.take when len >= m and len <= n" $ runParserTC_temp ptt takeBetweenPass prop ("P.takeBetween = Prelude.take when len >= m and len <= n and fail" ++ "otherwise fail") $ runParserTC_temp ptt takeBetween prop "P.take = Prelude.take" $ runParserTC_temp ptt Streamly.Test.Data.Parser.Common.take prop "P.takeEQ = Prelude.take when len >= n" $ runParserTC_temp ptt takeEQPass prop "P.takeEQ = Prelude.take when len >= n and fail otherwise" $ runParserTC_temp ptt Streamly.Test.Data.Parser.Common.takeEQ prop "P.takeGE n ls = ls when len >= n" $ runParserTC_temp ptt takeGEPass prop "P.takeGE n ls = ls when len >= n and fail otherwise" $ runParserTC_temp ptt Streamly.Test.Data.Parser.Common.takeGE prop "lookAhead . take n >> lookAhead . take n = lookAhead . take n" $ runParserTC_temp ptt lookAheadPass -- prop "Fail when stream length exceeded" lookAheadFail prop "lookAhead . take n >> lookAhead . take n = lookAhead . take n, else fail" $ runParserTC_temp ptt lookAhead prop ("P.takeStartBy pred = head : Prelude.takeWhile (not . pred)" ++ " tail") $ runParserTC ptt takeStartBy prop "P.takeWhile = Prelude.takeWhile" $ runParserTC ptt Streamly.Test.Data.Parser.Common.takeWhile prop ("P.takeWhile1 = Prelude.takeWhile if taken something," ++ " else check why failed") $ runParserTC ptt takeWhile1 prop "takeWhileP prd P.take = takeWhileMaxLen prd" $ runParserTC ptt takeWhileP prop ("P.takeP = Prelude.take") $ runParserTC ptt takeP prop "P.groupBy = Prelude.head . Prelude.groupBy" $ runParserTC_temp ptt groupBy prop "groupByRolling" $ runParserTC_temp ptt groupByRolling prop "many (P.wordBy ' ') = words'" $ runParserTC_temp ptt wordBy -- prop "choice" choice prop "parse 0, then 1, else fail" $ runParserTC_temp ptt splitWith prop "fail due to die as left parser" $ runParserTC_temp ptt splitWithFailLeft prop "fail due to die as right parser" $ runParserTC_temp ptt splitWithFailRight prop "fail due to die as both parsers" $ runParserTC_temp ptt splitWithFailBoth -- prop "" teeWithPass -- prop "" teeWithFailLeft -- prop "" teeWithFailRight -- prop "" teeWithFailBoth -- prop "deintercalate" deintercalate -- prop "" shortestPass -- prop "" shortestFailLeft -- prop "" shortestFailRight -- prop "" shortestFailBoth prop ("P.many concatFold $ P.takeEndBy_ (== 1) FL.toList =" ++ "Prelude.filter (== 0)") $ runParserTC_temp ptt many prop "[] due to parser being die" $ runParserTC_temp ptt many_empty prop ("P.some concatFold $ P.takeEndBy_ (== 1) FL.toList =" ++ "Prelude.filter (== 0)") $ runParserTC_temp ptt some prop "fail due to parser being die" $ runParserTC_temp ptt someFail prop "takeEndBy_" $ runParserTC ptt takeEndBy_ prop "takeEndByOrMax_" $ runParserTC ptt takeEndByOrMax_ prop "takeEndBy1" $ runParserTC_temp ptt takeEndBy1 prop "takeEndByEsc" $ runParserTC_temp ptt takeEndByEsc prop "takeFramedByEsc_" $ runParserTC_temp ptt takeFramedByEsc_ prop "takeFramedByEsc_Pass" $ runParserTC_temp ptt takeFramedByEsc_Pass prop "takeFramedByEsc_Fail1" $ runParserTC_temp ptt takeFramedByEsc_Fail1 prop "takeFramedByEsc_Fail2" $ runParserTC_temp ptt takeFramedByEsc_Fail2 prop "takeFramedByEsc_Fail3" $ runParserTC_temp ptt takeFramedByEsc_Fail3 prop "takeStartBy_" $ runParserTC_temp ptt takeStartBy_ runParserTC_temp ptt takeProperties main :: Spec main = do -- We keep Parser and ParserK tests in the same (Parser) executable for 2 -- reasons: -- 1. We almost always write Parser tests hence we prioritize Parser over -- ParserK -- 2. This results in minimal compilation overhead compared to duplicating -- or keeping the common part in the library. -- 2.1. Duplication will result in compilation of this code twice -- 2.2. Keeping the common part in the library will compile the Parser -- code even when it's not necessary. For example, if we are running -- non-parser test suites. -- -- One problem is that this module becomes very big for compilation. We can -- break this further and keep them as a part of "other-modules" in -- Test.Parser test-suite. mainCommon TMParserStream mainCommon TMParserKStreamKChunks mainCommon TMParserKStreamK mainCommon TMParserKStreamKChunksGeneric