packages feed

iteratee 0.2.1 → 0.2.3

raw patch · 5 files changed

+438/−9 lines, 5 filesdep +QuickCheckdep +test-frameworkdep +test-framework-quickcheck2dep ~basenew-component:exe:testIterateePVP ok

version bump matches the API change (PVP)

Dependencies added: QuickCheck, test-framework, test-framework-quickcheck2

Dependency ranges changed: base

API changes (from Hackage documentation)

+ Data.Iteratee.Base: filter :: (ListLike (s el) el, Monad m) => (el -> Bool) -> EnumeratorN s el s el m a
+ Data.Iteratee.Base: foldl :: (ListLike (s el) el, FoldableLL (s el) el, Monad m) => (a -> el -> a) -> a -> IterateeG s el m a
+ Data.Iteratee.Base: foldl' :: (ListLike (s el) el, FoldableLL (s el) el, Monad m) => (a -> el -> a) -> a -> IterateeG s el m a
+ Data.Iteratee.Base: foldl1 :: (ListLike (s el) el, FoldableLL (s el) el, Monad m) => (el -> el -> el) -> IterateeG s el m el
+ Data.Iteratee.Base: length :: (Num a, ListLike (s el) el, Monad m) => IterateeG s el m a

Files

iteratee.cabal view
@@ -1,5 +1,5 @@ Name:		iteratee-Version:        0.2.1+Version:        0.2.3 Cabal-Version:  >= 1.2 Description:	The IterateeGM monad provides strict, safe, and functional                 I/O.  In addition to pure Iteratee processors, file IO and @@ -53,4 +53,18 @@                         Data.Iteratee.WrappedByteString  other-modules:         Data.Iteratee.IO.Fd                         Data.Iteratee.IO.Handle++Executable testIteratee+  Hs-Source-Dirs:  src, tests+  Main-Is:         testIteratee.hs+  Other-Modules:   QCUtils+  if flag(buildTests)+    Build-Depends: QuickCheck >= 2 && < 3, test-framework >= 0.2 && < 0.3, test-framework-quickcheck2 >= 0.2 && < 0.3+  Else+    Executable:    False+    Buildable:     False+  If flag(splitBase)+    Build-Depends: base >= 3 && < 5+  Else+    Build-Depends: base >= 1 && < 2 
src/Data/Iteratee/Base.hs view
@@ -34,12 +34,17 @@   heads,   peek,   skipToEof,-  seek,-  -- ** Advanced iteratee combinators+  length,+  -- ** Nested iteratee combinators   take,   takeR,   mapStream,   convStream,+  filter,+  -- ** Folds+  foldl,+  foldl',+  foldl1,   -- * Enumerators   enumEof,   enumErr,@@ -47,15 +52,17 @@   enumPure1Chunk,   enumPureNChunk,   -- * Misc.+  seek,   FileOffset ) where -import Prelude hiding (head, drop, dropWhile, take, break)+import Prelude hiding (head, drop, dropWhile, take, break, foldl, foldl1, length, filter) import qualified Prelude as P  import qualified Data.Iteratee.Base.StreamChunk as SC import qualified Data.ListLike as LL+import qualified Data.ListLike.FoldableLL as FLL import Data.Iteratee.IO.Base import Control.Monad import Control.Applicative@@ -136,6 +143,7 @@  -- Useful combinators for implementing iteratees and enumerators +-- | Lift an IterGV result into an 'IterateeG' liftI :: (Monad m, SC.StreamChunk s el) =>          IterGV s el m a -> IterateeG s el m a liftI (Cont k Nothing)     = k@@ -146,12 +154,15 @@   check str (Chunk str') = return $ Done a (Chunk $ str `mappend` str')   check _str e@(EOF _)   = return $ Done a e +-- | Run an 'IterateeG' and get the result.  An 'EOF' is sent to the+-- iteratee as it is run. run :: (Monad m, SC.StreamChunk s el) => IterateeG s el m a -> m a run iter = runIter iter (EOF Nothing) >>= \res ->   case res of     Done x _ -> return x     Cont _ e -> error $ "control message: " ++ show e +-- | Check if a stream has finished ('EOF'). isFinished :: (SC.StreamChunk s el, Monad m) =>               IterateeG s el m (Maybe ErrMsg) isFinished = IterateeG check@@ -159,8 +170,8 @@   check s@(EOF e) = return $ Done (Just $ fromMaybe (Err "EOF") e) s   check s         = return $ Done Nothing s --- |If the iteratee (IterGV) has finished, return its value.  If it has not--- finished then apply it to the given EnumeratorGM.+-- |If the iteratee ('IterGV') has finished, return its value.  If it has not+-- finished then apply it to the given 'EnumeratorGM'. -- If in error, throw the error. checkIfDone :: (SC.StreamChunk s el, Monad m) =>                (IterateeG s el m a -> m (IterateeG s el m a)) ->@@ -252,6 +263,9 @@  -- |Check if an iteratee produces an error. -- Returns 'Right a' if it completes without errors, otherwise 'Left ErrMsg'+-- checkErr is useful for iteratees that may not terminate, such as 'head'+-- with an empty stream.  In particular, it enables them to be used with+-- 'convStream'. checkErr :: (Monad m, SC.StreamChunk s el) =>               IterateeG s el m a ->               IterateeG s el m (Either ErrMsg a)@@ -290,7 +304,7 @@  -- |The identity iterator.  Doesn't do anything. identity :: (Monad m) => IterateeG s el m ()-identity = IterateeG (return . Done ())+identity = return ()   -- |Attempt to read the next element of the stream and return it@@ -368,6 +382,18 @@   step (Chunk str)       = return $ Done () (Chunk (LL.drop n str))   step stream            = return $ Done () stream ++-- |Return the total length of the stream+length :: (Num a, LL.ListLike (s el) el, Monad m) => IterateeG s el m a+length = length' 0+  where+  length' = IterateeG . step+  step i (Chunk xs) = return $ Cont+                               (length' $! i + fromIntegral (LL.length xs))+                               Nothing+  step i stream     = return $ Done i stream++ -- --------------------------------------------------- -- The converters show a different way of composing two iteratees: -- `vertical' rather than `horizontal'@@ -464,6 +490,55 @@   docase (Cont _ (Just e)) = return $ throwErr e  {-# SPECIALIZE convStream :: IterateeG s el IO (Maybe (s' el')) -> EnumeratorN s el s' el' IO a #-}+++-- |Creates an enumerator with only elements from the stream that+-- satisfy the predicate function.+filter :: (LL.ListLike (s el) el, Monad m) =>+          (el -> Bool) ->+          EnumeratorN s el s el m a+filter p = convStream f'+  where+  f' = IterateeG step+  step (Chunk xs) | LL.null xs = return $ Cont f' Nothing+  step (Chunk xs) = return $ Done (Just $ LL.filter p xs) mempty+  step stream     = return $ Done Nothing stream++-- ------------------------------------------------------------------------+-- Folds++-- | Left-associative fold.+foldl :: (LL.ListLike (s el) el, FLL.FoldableLL (s el) el, Monad m) =>+         (a -> el -> a) ->+         a ->+         IterateeG s el m a+foldl f i = IterateeG step+  where+  step (Chunk xs) | LL.null xs = return $ Cont (foldl f i) Nothing+  step (Chunk xs) = return $ Cont (foldl f (FLL.foldl f i xs)) Nothing+  step stream     = return $ Done i stream++-- | Left-associative fold that is strict in the accumulator.+foldl' :: (LL.ListLike (s el) el, FLL.FoldableLL (s el) el, Monad m) =>+          (a -> el -> a) ->+          a ->+          IterateeG s el m a+foldl' f i = IterateeG step+  where+  step (Chunk xs) | LL.null xs = return $ Cont (foldl' f i) Nothing+  step (Chunk xs) = return $ Cont (foldl' f $! FLL.foldl' f i xs) Nothing+  step stream     = return $ Done i stream++-- | Variant of foldl with no base case.  Requires at least one element+--   in the stream.+foldl1 :: (LL.ListLike (s el) el, FLL.FoldableLL (s el) el, Monad m) =>+          (el -> el -> el) ->+          IterateeG s el m el+foldl1 f = IterateeG step+  where+  step (Chunk xs) | LL.null xs = return $ Cont (foldl1 f) Nothing+  step (Chunk xs) = return $ Cont (foldl f (FLL.foldl1 f xs)) Nothing+  step stream     = return $ Cont (foldl1 f) (Just (setEOF stream))  -- ------------------------------------------------------------------------ -- Enumerators
src/Data/Iteratee/Codecs/Wave.hs view
@@ -27,6 +27,7 @@ ) where +import Prelude as P import Data.Iteratee.Base import qualified Data.Iteratee.Base as Iter import Data.Iteratee.Binary@@ -80,7 +81,7 @@ waveChunk str   | str == "fmt " = Just WAVE_FMT   | str == "data" = Just WAVE_DATA-  | length str == 4 = Just $ WAVE_OTHER str+  | P.length str == 4 = Just $ WAVE_OTHER str   | otherwise = Nothing  -- |Convert a WAVE_CHUNK to the representative string@@ -154,7 +155,7 @@  loadDict :: [(Int, WAVE_CHUNK, Int)] ->                IterateeG L Word8 IO (Maybe WAVEDict)-loadDict = foldl read_entry (return (Just IM.empty))+loadDict = P.foldl read_entry (return (Just IM.empty))   where   read_entry dictM (offset, typ, count) = dictM >>=     maybe (return Nothing) (\dict -> do
+ tests/QCUtils.hs view
@@ -0,0 +1,42 @@+{-# LANGUAGE FlexibleInstances, FlexibleContexts #-}++module QCUtils where++import Test.QuickCheck+import Test.QuickCheck.Arbitrary+import Test.QuickCheck.Gen++import Data.Iteratee+import qualified Data.Iteratee as I+import Data.Iteratee.Base.StreamChunk (StreamChunk)+import Control.Monad.Identity++-- Show instance+instance (Show a, StreamChunk s el) => Show (IterateeG s el Identity a) where+  show = (++) "<<Iteratee>> " . show . runIdentity . run++-- Arbitrary instances++instance Arbitrary ErrMsg where+  arbitrary = do+    err <- arbitrary+    n <- arbitrary :: Gen Int+    elements [Err err, Seek (fromIntegral n)]++instance Arbitrary (c el) => Arbitrary (StreamG c el) where+  arbitrary = do+    err <- arbitrary+    xs <- arbitrary+    elements [EOF err, Chunk xs]++instance (Num a, Ord a, Arbitrary a, Monad m) => Arbitrary (IterateeG [] a m [a]) where+  arbitrary = do+    n <- suchThat arbitrary (>0)+    ns <- arbitrary+    elements [+              I.drop n >> stream2list+              ,I.break (< 5)+              ,I.heads ns >> stream2list+              ,I.peek >> stream2list+              ]+
+ tests/testIteratee.hs view
@@ -0,0 +1,297 @@+{-# OPTIONS_GHC -O #-}+{-# LANGUAGE NoMonomorphismRestriction #-}++import QCUtils++import Test.Framework (defaultMain, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)++import Test.QuickCheck++import Data.Iteratee hiding (head, break)+import qualified Data.Iteratee.Char as IC+import qualified Data.Iteratee as Iter+import qualified Data.Iteratee.Base.StreamChunk as SC+import Control.Monad.Identity+import Data.Monoid+import qualified Data.ListLike as LL++import Text.Printf (printf)+import System.Environment (getArgs)++instance Show (a -> b) where+  show _ = "<<function>>"++-- ---------------------------------------------+-- StreamG instances++type ST = StreamG [] Int++prop_eq str = str == str+  where types = (str :: ST)++prop_mempty = mempty == (Chunk [] :: StreamG [] Int)++prop_mappend str1 str2 | isChunk str1 && isChunk str2 =+  str1 `mappend` str2 == Chunk (chunkData str1 ++ chunkData str2)+prop_mappend str1 str2 = isEOF $ str1 `mappend` str2+  where types = (str1 :: ST, str2 :: ST)++prop_functor str@(EOF _) f = isEOF $ fmap f str+prop_functor str@(Chunk xs) f = fmap f str == Chunk (fmap f xs)+  where types = (str :: ST, f :: Int -> Integer)++prop_mappend2 str = str `mappend` mempty == mempty `mappend` str+  where types = (str :: ST)+++isChunk (Chunk _) = True+isChunk (EOF _)   = False++chunkData (Chunk xs) = xs++isEOF (EOF _)   = True+isEOF (Chunk _) = False++-- ---------------------------------------------+-- Iteratee instances++runner0 = runIdentity . Iter.run+runner1 = runIdentity . Iter.run . runIdentity++prop_iterFmap xs f a = runner1 (enumPure1Chunk xs (fmap f $ return a))+                     == runner1 (enumPure1Chunk xs (return $ f a))+  where types = (xs :: [Int], f :: Int -> Int, a :: Int)++prop_iterFmap2 xs f i = runner1 (enumPure1Chunk xs (fmap f i))+                      == f (runner1 (enumPure1Chunk xs i))+  where types = (xs :: [Int], i :: I, f :: [Int] -> [Int])++prop_iterMonad1 xs a f = runner1 (enumPureNChunk xs 1 (return a >>= f))+                       == runner1 (enumPure1Chunk xs (f a))+  where types = (xs :: [Int], a :: Int, f :: Int -> I)++prop_iterMonad2 m xs = runner1 (enumPureNChunk xs 1 (m >>= return))+                     == runner1 (enumPure1Chunk xs m)+  where types = (xs :: [Int], m :: I)++prop_iterMonad3 m f g xs = runner1 (enumPureNChunk xs 1 ((m >>= f) >>= g))+                         == runner1 (enumPure1Chunk xs (m >>= (\x -> f x >>= g)))+  where types = (xs :: [Int], m :: I, f :: [Int] -> I, g :: [Int] -> I)++-- ---------------------------------------------+-- List <-> Stream++prop_list xs = runner1 (enumPure1Chunk xs stream2list) == xs+  where types = xs :: [Int]++prop_clist xs n = n > 0 ==> runner1 (enumPureNChunk xs n stream2list) == xs+  where types = xs :: [Int]++prop_break f xs = runner1 (enumPure1Chunk xs (Iter.break f)) == fst (break f xs)+  where types = xs :: [Int]++prop_break2 f xs = runner1 (enumPure1Chunk xs (Iter.break f >> stream2list)) == snd (break f xs)+  where types = xs :: [Int]++prop_head xs = length xs > 0 ==> runner1 (enumPure1Chunk xs Iter.head) == head xs+  where types = xs :: [Int]++prop_head2 xs = length xs > 0 ==> runner1 (enumPure1Chunk xs (Iter.head >> stream2list)) == tail xs+  where types = xs :: [Int]++prop_heads xs = runner1 (enumPure1Chunk xs $ heads xs) == length xs+  where types = xs :: [Int]++prop_heads2 xs = runner1 (enumPure1Chunk xs $ heads [] >>= \c ->+                          stream2list >>= \s -> return (c,s))+                 == (0, xs)+  where types = xs :: [Int]++prop_peek xs = runner1 (enumPure1Chunk xs peek) == sHead xs+  where+  types = xs :: [Int]+  sHead [] = Nothing+  sHead (x:_) = Just x++prop_peek2 xs = runner1 (enumPure1Chunk xs (peek >> stream2list)) == xs+  where types = xs :: [Int]++prop_skip xs = runner1 (enumPure1Chunk xs (skipToEof >> stream2list)) == []+  where types = xs :: [Int]++-- ---------------------------------------------+-- Simple enumerator tests++type I = IterateeG [] Int Identity [Int]++prop_enumChunks n xs i = n > 0  ==>+  runner1 (enumPure1Chunk xs i) == runner1 (enumPureNChunk xs n i)+  where types = (n :: Int, xs :: [Int], i :: I)++prop_app1 xs ys i = runner1 (enumPure1Chunk ys (joinIM $ enumPure1Chunk xs i))+                    == runner1 (enumPure1Chunk (xs ++ ys) i)+  where types = (xs :: [Int], ys :: [Int], i :: I)++prop_app2 xs ys = runner1 ((enumPure1Chunk xs >. enumPure1Chunk ys) stream2list)+                  == runner1 (enumPure1Chunk (xs ++ ys) stream2list)+  where types = (xs :: [Int], ys :: [Int])++prop_app3 xs ys i = runner1 ((enumPure1Chunk xs >. enumPure1Chunk ys) i)+                    == runner1 (enumPure1Chunk (xs ++ ys) i)+  where types = (xs :: [Int], ys :: [Int], i :: I)++prop_eof xs ys i = runner1 (enumPure1Chunk ys $ runIdentity $+                           (enumPure1Chunk xs >. enumEof) i)+                 == runner1 (enumPure1Chunk xs i)+  where types = (xs :: [Int], ys :: [Int], i :: I)++prop_isFinished = runner1 (enumEof (isFinished :: IterateeG [] Int Identity (Maybe ErrMsg))) == Just (Err "EOF")++prop_isFinished2 = runner1 (enumErr "Error" (isFinished :: IterateeG [] Int Identity (Maybe ErrMsg))) == Just (Err "Error")++prop_null xs i = runner1 (enumPure1Chunk xs =<< enumPure1Chunk [] i)+                 == runner1 (enumPure1Chunk xs i)+  where types = (xs :: [Int], i :: I)++prop_nullH xs = length xs > 0 ==>+                runner1 (enumPure1Chunk xs =<< enumPure1Chunk [] Iter.head)+                == runner1 (enumPure1Chunk xs Iter.head)+  where types = (xs :: [Int])+-- ---------------------------------------------+-- Nested Iteratees++-- take, mapStream, convStream, and takeR++runner2 = runIdentity . run . runner1++prop_mapStream xs i = runner2 (enumPure1Chunk xs $ mapStream id i)+                      == runner1 (enumPure1Chunk xs i)+  where types = (i :: I, xs :: [Int])++prop_mapStream2 xs n i = n > 0 ==>+                         runner2 (enumPureNChunk xs n $ mapStream id i)+                         == runner1 (enumPure1Chunk xs i)+  where types = (i :: I, xs :: [Int])++prop_mapjoin xs i =+  runIdentity (run (joinI . runIdentity $ enumPure1Chunk xs $ mapStream id i))+  == runner1 (enumPure1Chunk xs i)+  where types = (i :: I, xs :: [Int])+++convId :: (SC.StreamChunk s el, Monad m) => IterateeG s el m (Maybe (s el))+convId = IterateeG (\str -> case str of+  s@(Chunk xs) | LL.null xs -> return $ Cont convId Nothing+  s@(Chunk xs) -> return $ Done (Just xs) (Chunk mempty)+  s@(EOF e)   -> return $ Done Nothing (EOF e)+  )++prop_convId xs = runner1 (enumPure1Chunk xs convId) == Just xs+  where types = xs :: [Int]++prop_convstream xs i = length xs > 0 ==>+                       runner2 (enumPure1Chunk xs $ convStream convId i)+                       == runner1 (enumPure1Chunk xs i)+  where types = (xs :: [Int], i :: I)++prop_convstream2 xs = length xs > 0 ==>+                      runner2 (enumPure1Chunk xs $ convStream convId Iter.head)+                      == runner1 (enumPure1Chunk xs Iter.head)+  where types = (xs :: [Int])++prop_convstream3 xs = length xs > 0 ==>+                      runner2 (enumPure1Chunk xs $ convStream convId stream2list)+                      == runner1 (enumPure1Chunk xs stream2list)+  where types = (xs :: [Int])++prop_take xs n = n >= 0 ==>+                 runner2 (enumPure1Chunk xs $ Iter.take n stream2list)+                 == runner1 (enumPure1Chunk (Prelude.take n xs) stream2list)+  where types = (xs :: [Int])++prop_take2 xs n = n > 0 ==>+                  runner2 (enumPure1Chunk xs $ Iter.take n peek)+                  == runner1 (enumPure1Chunk (Prelude.take n xs) peek)+  where types = (xs :: [Int])++prop_takeR xs n = n >= 0 ==>+                  runner2 (enumPure1Chunk xs $ Iter.take n stream2list)+                  == runner2 (enumPure1Chunk xs $ takeR n stream2list)+  where types = (xs :: [Int])++-- ---------------------------------------------+-- Data.Iteratee.Char++{-+-- this isn't true, since lines "\r" returns ["\r"], and IC.line should+-- return Right "".  Not sure what a real test would be...+prop_line xs = length xs > 0 ==>+               fromEither (runner1 (enumPure1Chunk xs $ IC.line))+               == head (lines xs)+  where+  types = xs :: [Char]+  fromEither (Left l)  = l+  fromEither (Right l) = l+-}++-- ---------------------------------------------+tests = [+  testGroup "Elementary" [+    testProperty "list" prop_list+    ,testProperty "chunkList" prop_clist]+  ,testGroup "StreamG tests" [+    testProperty "mempty" prop_mempty+    ,testProperty "mappend" prop_mappend+    ,testProperty "mappend associates" prop_mappend2+    ,testProperty "functor" prop_functor+    ,testProperty "eq" prop_eq+  ]+  ,testGroup "Simple Iteratees" [+    testProperty "break" prop_break+    ,testProperty "break remaineder" prop_break2+    ,testProperty "head" prop_head+    ,testProperty "head remainder" prop_head2+    ,testProperty "heads" prop_heads+    ,testProperty "null heads" prop_heads2+    ,testProperty "peek" prop_peek+    ,testProperty "peek2" prop_peek2+    ,testProperty "skipToEof" prop_skip+    ,testProperty "iteratee Functor 1" prop_iterFmap+    ,testProperty "iteratee Functor 2" prop_iterFmap2+    ,testProperty "iteratee Monad LI" prop_iterMonad1+    ,testProperty "iteratee Monad RI" prop_iterMonad2+    ,testProperty "iteratee Monad Assc" prop_iterMonad3+    ]+  ,testGroup "Simple Enumerators/Combinators" [+    testProperty "enumPureNChunk" prop_enumChunks+    ,testProperty "enum append 1" prop_app1+    ,testProperty "enum sequencing" prop_app2+    ,testProperty "enum sequencing 2" prop_app3+    ,testProperty "enumEof" prop_eof+    ,testProperty "isFinished" prop_isFinished+    ,testProperty "isFinished error" prop_isFinished2+    ,testProperty "null data idempotence" prop_null+    ,testProperty "null data head idempotence" prop_nullH+    ]+  ,testGroup "Nested iteratees" [+    testProperty "mapStream identity" prop_mapStream+    ,testProperty "mapStream identity 2" prop_mapStream2+    ,testProperty "mapStream identity joinI" prop_mapjoin+    ,testProperty "take" prop_take+    ,testProperty "take (finished iteratee)" prop_take2+    ,testProperty "takeR" prop_takeR+    ,testProperty "convStream EOF" prop_convstream2+    ,testProperty "convStream identity" prop_convstream+    ,testProperty "convStream identity 2" prop_convstream3+    ]+  ,testGroup "Data.Iteratee.Char" [+    --testProperty "line" prop_line+    ]+  ]++------------------------------------------------------------------------+-- The entry point++main = defaultMain tests+