{-# LANGUAGE ScopedTypeVariables #-} module Main where import qualified Test.Tasty as Tasty import Test.Tasty.HUnit (testCase, (@=?)) import qualified Test.Tasty.Runners as Tasty import Test.Tasty.QuickCheck (forAll, testProperty, arbitrary) import Control.Exception (throwIO) import Control.Monad import Control.Monad.Trans.Maybe import Control.Monad.Trans.State.Strict (StateT, evalStateT, runStateT) import qualified Data.Binary as Bin import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import Data.Functor.Identity (runIdentity) import Data.Maybe import Lens.Family (view) import Lens.Family.State.Strict (zoom) import Pipes import qualified Pipes.Binary as PBin import qualified Pipes.Parse as PP import qualified Pipes.Prelude as P -------------------------------------------------------------------------------- main :: IO () main = Tasty.defaultMainWithIngredients [ Tasty.consoleTestReporter , Tasty.listingTests ] tests tests :: Tasty.TestTree tests = Tasty.testGroup "root" [ testFunctorLaws , testPipesBinary ] testFunctorLaws :: Tasty.TestTree testFunctorLaws = Tasty.testGroup "Functor laws (sample test)" [ testCase "fmap id Nothing = Nothing" $ do fmap id Nothing @=? (Nothing :: Maybe ()) , testProperty "fmap id = id" $ do forAll arbitrary $ \(x :: [Int]) -> fmap id x == id x , testCase "fmap (f . g) Nothing = (fmap f . fmap g) Nothing" $ do fmap (not . not) Nothing @=? (fmap not . fmap not) (Nothing :: Maybe Bool) , testProperty "fmap (f . g) = fmap f . fmap g" $ do forAll arbitrary $ \(x :: [Int]) -> fmap (odd . succ) x == (fmap odd . fmap succ) x ] -- Just an arbitrary type that can be generated by QuickCheck. type FunnyType = (String, (Double, (Int, (Maybe Int, Either Bool Int)))) testPipesBinary :: Tasty.TestTree testPipesBinary = Tasty.testGroup "pipes-binary" [ testProperty "Pipes.Binary.encode ~ Data.Binary.encode" $ do forAll arbitrary $ \(x :: FunnyType) -> BL.toStrict (Bin.encode x) == B.concat (P.toList (PBin.encode x)) , testProperty "Pipes.Binary.decodeL ~ Data.Binary.decode" $ do forAll arbitrary $ \(x :: FunnyType) -> let bl = Bin.encode x bs = BL.toStrict bl o1 = Bin.decodeOrFail bl (o2,s2) = fmap (B.concat . P.toList) (runIdentity $ runStateT PBin.decodeL (yield bs)) in case (o1, o2) of (Left (s1,n1,_), Left (PBin.DecodingError n2 _)) -> n1 == n2 && BL.toStrict s1 == s2 (Right (s1,n1,a1), Right (n2,a2)) -> n1 == n2 && BL.toStrict s1 == s2 && a1 == (a2 :: FunnyType) _ -> False , testProperty "Pipes.Binary.decodeL ~ Pipes.Binary.decode" $ do forAll arbitrary $ \(x :: FunnyType) -> let bs = BL.toStrict $ Bin.encode x o1 = runIdentity $ evalStateT PBin.decodeL (yield bs) o2 = runIdentity $ evalStateT PBin.decode (yield bs) in fmap snd o1 == (o2 :: Either PBin.DecodingError FunnyType) , testProperty "Pipes.Binary.decoded zoom" $ do forAll arbitrary $ \amx0 amx1 amx2 amx3 amx4 amx5 amx6 -> let xs :: [FunnyType] -- I get more tests cases this way. xs = [amx0, amx1, amx2, amx3, amx4, amx5, amx6] >>= maybe [] id dec0 :: Monad m => MaybeT (StateT (Producer B.ByteString m a) m) () dec0 = do case xs of [] -> do mx0' <- lift $ zoom PBin.decoded PP.draw guard $ isNothing (mx0' :: Maybe FunnyType) rest <- lift $ zoom PBin.decoded PP.drawAll guard $ null (rest :: [FunnyType]) (x0:x1:x2:xrest) -> do x0x1' <- lift $ zoom (PBin.decoded . PP.splitAt 2) PP.drawAll guard $ [x0,x1] == x0x1' ex2' <- lift $ PBin.decode guard $ Right x2 == ex2' mx3' <- lift $ zoom PBin.decoded PP.draw case (mx3', xrest) of (Nothing, []) -> return () (Just x3', (x3:rest)) | x3' == x3 -> do rest' <- lift $ zoom PBin.decoded PP.drawAll guard $ rest == rest' _ -> mzero (x0:xrest) -> do mx0' <- lift $ zoom PBin.decoded PP.draw guard $ Just x0 == mx0' xrest' <- lift $ zoom PBin.decoded PP.drawAll guard $ xrest == xrest' p0 = for (each xs) PBin.encode in isJust $ runIdentity $ evalStateT (runMaybeT dec0) p0 , testCase "Decoding an empty stream shouldn't fail" (do let p :: Monad m => Producer Int m (Either (PBin.DecodingError, Producer B.ByteString m ()) ()) p = view PBin.decoded (yield B.empty) x <- runEffect (p >-> P.print) case x of Left (err, _) -> throwIO err Right r -> return r ) ]