{-# LANGUAGE BangPatterns #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} module System.IO.Streams.Tests.Combinators (tests) where ------------------------------------------------------------------------------ import Control.Applicative import Control.Monad hiding (filterM, mapM, mapM_) import qualified Control.Monad as CM import Data.IORef import Data.List hiding (drop, filter, take, unzip, zip, zipWith) import Prelude hiding (drop, filter, mapM, mapM_, read, take, unzip, zip, zipWith) import qualified Prelude import System.IO.Streams hiding (all, any, maximum, minimum) import qualified System.IO.Streams as S import Test.Framework import Test.Framework.Providers.HUnit import Test.Framework.Providers.QuickCheck2 import Test.HUnit hiding (Test) import Test.QuickCheck hiding (output) import Test.QuickCheck.Monadic ------------------------------------------------------------------------------ import System.IO.Streams.Tests.Common ------------------------------------------------------------------------------ tests :: [Test] tests = [ testFilter , testFilterM , testFilterOutput , testFilterOutputM , testFoldMWorksTwice , testFold , testFoldM , testUnfoldM , testPredicates , testMap , testContramap , testMapM , testMapM_ , testContramapM_ , testSkipToEof , testZip , testZipWith , testZipWithM , testUnzip , testTake , testDrop , testGive , testIgnore , testIgnoreEof , testAtEnd ] ------------------------------------------------------------------------------ testFoldMWorksTwice :: Test testFoldMWorksTwice = testCase "combinators/foldMWorksTwice" $ do (os, grab) <- nullOutput >>= outputFoldM f (0::Int) let l = [1,2,3] fromList l >>= supplyTo os m <- grab assertEqual "foldm1" (sum l) m let l2 = [4,5,6] fromList l2 >>= supplyTo os m2 <- grab assertEqual "foldm2" (sum l2) m2 (is, grab2) <- fromList l >>= inputFoldM f (0::Int) _ <- read is x <- grab2 assertEqual "foldm3" 1 x _ <- read is >> read is y <- grab2 assertEqual "foldm4" 5 y read is >>= assertEqual "eof" Nothing where f a b = return (a+b) ------------------------------------------------------------------------------ testMapM :: Test testMapM = testCase "combinators/mapM" $ do is <- fromList [1,2,3::Int] >>= mapM (return . (1+)) l <- toList is assertEqual "mapM" [2,3,4] l ------------------------------------------------------------------------------ testMap :: Test testMap = testCase "combinators/map" $ do is <- fromList [1,2,3::Int] >>= S.map (1+) l <- toList is assertEqual "map" [2,3,4] l ------------------------------------------------------------------------------ testContramap :: Test testContramap = testCase "combinators/contramap" $ do is <- fromList [1,2,3::Int] l <- outputToList (contramap (+1) >=> connect is) assertEqual "contramap" [2,3,4] l ------------------------------------------------------------------------------ testMapM_ :: Test testMapM_ = testCase "combinators/mapM_" $ do ref <- newIORef 0 is <- fromList [1,2,3::Int] >>= mapM_ (modifyIORef ref . (+)) _ <- toList is readIORef ref >>= assertEqual "mapM_" 6 ------------------------------------------------------------------------------ testContramapM_ :: Test testContramapM_ = testCase "combinators/contramapM_" $ do ref <- newIORef 0 is <- fromList [1,2,3::Int] _ <- outputToList (contramapM_ (modifyIORef ref . (+)) >=> connect is) readIORef ref >>= assertEqual "contramapM_" 6 ------------------------------------------------------------------------------ testSkipToEof :: Test testSkipToEof = testCase "combinators/skipToEof" $ do is <- fromList [1,2,3::Int] !_ <- skipToEof is x <- read is assertEqual "skipToEof" Nothing x ------------------------------------------------------------------------------ testFilter :: Test testFilter = testCase "combinators/filter" $ do is <- fromList [1..10::Int] is' <- filter even is read is' >>= assertEqual "read1" (Just 2) unRead 3 is' peek is >>= assertEqual "pushback" (Just 3) toList is' >>= assertEqual "rest" [4,6..10] unRead 20 is' peek is >>= assertEqual "pushback2" (Just 20) toList is' >>= assertEqual "rest2" [20] toList is' >>= assertEqual "eof" [] ------------------------------------------------------------------------------ testFilterM :: Test testFilterM = testCase "combinators/filterM" $ do is <- fromList [1..10::Int] is' <- filterM (return . even) is read is' >>= assertEqual "read1" (Just 2) unRead 3 is' peek is >>= assertEqual "pushback" (Just 3) toList is' >>= assertEqual "rest" [4,6..10] unRead 20 is' peek is >>= assertEqual "pushback2" (Just 20) toList is' >>= assertEqual "rest2" [20] toList is' >>= assertEqual "eof" [] ------------------------------------------------------------------------------ testFilterOutput :: Test testFilterOutput = testCase "combinators/filterOutput" $ do is <- fromList [1..10::Int] l <- outputToList (\os -> filterOutput even os >>= connect is) assertEqual "filterOutput" (Prelude.filter even [1..10]) l ------------------------------------------------------------------------------ testFilterOutputM :: Test testFilterOutputM = testCase "combinators/filterOutputM" $ do is <- fromList [1..10::Int] l <- outputToList (\os -> filterOutputM (return . even) os >>= connect is) assertEqual "filterOutputM" (Prelude.filter even [1..10]) l ------------------------------------------------------------------------------ testFold :: Test testFold = testCase "combinators/fold" $ do fromList [1..10::Int] >>= S.fold (+) 0 >>= assertEqual "fold1" (sum [1..10]) ------------------------------------------------------------------------------ testFoldM :: Test testFoldM = testCase "combinators/foldM" $ do fromList [1..10::Int] >>= S.foldM ((return .) . (+)) 0 >>= assertEqual "fold2" (sum [1..10]) ------------------------------------------------------------------------------ testUnfoldM :: Test testUnfoldM = testCase "combinators/unfoldM" $ do S.unfoldM gen 0 >>= toList >>= assertEqual "unfold" result where gen !n = return $! if n < 10 then Just (n, n + 1) else Nothing result = [0, 1 .. 9 :: Int] ------------------------------------------------------------------------------ data StreamPred = forall c . (Eq c, Show c) => P ([Int] -> c, InputStream Int -> IO c, String) testPredicates :: Test testPredicates = testProperty "combinators/predicates" $ monadicIO $ forAllM arbitrary prop where predicates :: [StreamPred] predicates = [ P (all even , S.all even , "all" ) , P (any even , S.any even , "any" ) , P (nl maximum , S.maximum , "maximum" ) , P (nl minimum , S.minimum , "minimum" ) ] nl f l = if null l then Nothing else Just (f l) prop :: [Int] -> PropertyM IO () prop l = liftQ $ CM.mapM_ (p l) predicates p :: [Int] -> StreamPred -> IO () p l (P (pPred, pStream, name)) = fromList l >>= pStream >>= assertEqual name (pPred l) ------------------------------------------------------------------------------ testZipWithM :: Test testZipWithM = testCase "combinators/zipWithM" $ do let l1 = [1 .. 10 :: Int] let l2 = [2 .. 10 :: Int] (join $ S.zipWithM ((return .) . (+)) <$> fromList l1 <*> fromList l2) >>= toList >>= assertEqual "zipWith1" (Prelude.zipWith (+) l1 l2) (join $ S.zipWithM ((return .) . (+)) <$> fromList l2 <*> fromList l1) >>= toList >>= assertEqual "zipWith1" (Prelude.zipWith (+) l2 l1) is1 <- fromList l1 is2 <- fromList l2 isZip <- S.zipWithM ((return .) . (+)) is1 is2 _ <- toList isZip read is1 >>= assertEqual "remainder" (Just 10) ------------------------------------------------------------------------------ testZipWith :: Test testZipWith = testCase "combinators/zipWith" $ do let l1 = [1 .. 10 :: Int] let l2 = [2 .. 10 :: Int] (join $ S.zipWith (+) <$> fromList l1 <*> fromList l2) >>= toList >>= assertEqual "zipWith1" (Prelude.zipWith (+) l1 l2) (join $ S.zipWith (+) <$> fromList l2 <*> fromList l1) >>= toList >>= assertEqual "zipWith1" (Prelude.zipWith (+) l2 l1) is1 <- fromList l1 is2 <- fromList l2 isZip <- S.zipWith (+) is1 is2 _ <- toList isZip read is1 >>= assertEqual "remainder" (Just 10) ------------------------------------------------------------------------------ testZip :: Test testZip = testCase "combinators/zip" $ do let l1 = [1 .. 10 :: Int] let l2 = [2 .. 10 :: Int] (join $ zip <$> fromList l1 <*> fromList l2) >>= toList >>= assertEqual "zip1" (Prelude.zip l1 l2) (join $ zip <$> fromList l2 <*> fromList l1) >>= toList >>= assertEqual "zip2" (Prelude.zip l2 l1) is1 <- fromList l1 is2 <- fromList l2 isZip <- zip is1 is2 _ <- toList isZip read is1 >>= assertEqual "remainder" (Just 10) ------------------------------------------------------------------------------ testUnzip :: Test testUnzip = testCase "combinators/unzip" $ do let l1 = [1 .. 10 :: Int] l2 = [2 .. 10 :: Int] l = Prelude.zip l1 l2 (is1, is2) <- fromList l >>= unzip toList is1 >>= assertEqual "unzip1-a" (fst $ Prelude.unzip l) toList is2 >>= assertEqual "unzip1-b" (snd $ Prelude.unzip l) read is1 >>= assertEqual "unzip1-read-a" Nothing read is2 >>= assertEqual "unzip1-read-b" Nothing (is3, is4) <- fromList l >>= unzip toList is4 >>= assertEqual "unzip2-b" (snd $ Prelude.unzip l) toList is3 >>= assertEqual "unzip2-a" (fst $ Prelude.unzip l) read is4 >>= assertEqual "unzip2-read-b" Nothing read is3 >>= assertEqual "unzip2-read" Nothing ------------------------------------------------------------------------------ testTake :: Test testTake = testCase "combinators/take" $ do fromList ([]::[Int]) >>= take 0 >>= toList >>= assertEqual "empty 0" [] fromList ([]::[Int]) >>= take 10 >>= toList >>= assertEqual "empty 10" [] forM_ [0..4] $ \n -> fromList [1,2,3::Int] >>= take n >>= toList >>= assertEqual ("for " ++ show n) (Prelude.take (fromEnum n) [1..3]) is <- fromList [1,2,3::Int] is' <- take 2 is void $ read is' unRead 0 is' peek is >>= assertEqual "pb" (Just 0) toList is' >>= assertEqual "toList" [0,2] unRead 7 is' peek is >>= assertEqual "pb2" (Just 7) toList is' >>= assertEqual "toList2" [7] ------------------------------------------------------------------------------ testDrop :: Test testDrop = testCase "combinators/drop" $ do fromList ([]::[Int]) >>= take 0 >>= toList >>= assertEqual "empty 0" [] fromList ([]::[Int]) >>= take 10 >>= toList >>= assertEqual "empty 10" [] forM_ [0..4] $ \n -> fromList [1,2,3::Int] >>= drop n >>= toList >>= assertEqual ("for " ++ show n) (Prelude.drop (fromEnum n) [1..3]) is <- fromList [1,2,3::Int] is' <- drop 1 is read is' >>= assertEqual "read" (Just 2) unRead 0 is' peek is >>= assertEqual "pb" (Just 0) toList is' >>= assertEqual "toList" [0,3] unRead 7 is' peek is >>= assertEqual "pb2" (Just 7) toList is' >>= assertEqual "toList2" [7] toList is' >>= assertEqual "toList2_empty" [] is2 <- fromList [1,2,3::Int] is2' <- drop 1 is2 read is2' >>= assertEqual "read2" (Just 2) unRead 2 is2' unRead 1 is2' unRead 0 is2' toList is2' >>= assertEqual "toList3" [2,3] ------------------------------------------------------------------------------ testGive :: Test testGive = testCase "combinators/give" $ forM_ [0..12] tgive where tgive n = fromList [1..10::Int] >>= \is -> outputToList (\os -> give n os >>= connect is) >>= assertEqual ("give" ++ show n) (Prelude.take (fromEnum n) [1..10]) ------------------------------------------------------------------------------ testIgnore :: Test testIgnore = testCase "combinators/ignore" $ forM_ [0..12] tign where tign n = fromList [1..10::Int] >>= \is -> outputToList (\os -> ignore n os >>= connect is) >>= assertEqual ("ignore" ++ show n) (Prelude.drop (fromEnum n) [1..10]) ------------------------------------------------------------------------------ testIgnoreEof :: Test testIgnoreEof = testCase "combinators/ignoreEof" $ do eofRef <- newIORef 0 chunkRef <- newIORef [] str0 <- S.makeOutputStream $ f eofRef chunkRef str <- S.ignoreEof str0 S.write (Just 0) str S.write Nothing str readIORef eofRef >>= assertEqual "eof ignored" (0::Int) readIORef chunkRef >>= assertEqual "input propagated" [0::Int] where f ref _ Nothing = modifyIORef ref (+1) f _ chunk (Just x) = modifyIORef chunk (++ [x]) ------------------------------------------------------------------------------ testAtEnd :: Test testAtEnd = testCase "combinators/atEndOfInput" $ do boolRef <- newIORef False is <- fromList [1,2,3::Int] >>= atEndOfInput (writeIORef boolRef True) unRead 0 is toList is >>= assertEqual "list" [0,1,2,3] readIORef boolRef >>= assertBool "ran" toList is >>= assertEqual "list 2" [] unRead 0 is toList is >>= assertEqual "list 3" [0]