streamly-0.11.1: test/Streamly/Test/Data/Parser/Common.hs
{-# 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