hspec-dirstream-0.2.0.0: src/Test/Hspec/Dirstream.hs
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StandaloneDeriving #-}
module Test.Hspec.Dirstream
( -- Functions to generate a `Spec`
testFiles
, testFilesIO
-- * Helper functions for dealing with file extensions
, F.extension
, Test.Hspec.Dirstream.hasExtension
) where
import Data.DirStream
import Data.Text (Text)
import qualified Filesystem.Path.CurrentOS as F
import Pipes
import qualified Pipes.Prelude as P
import Pipes.Safe
import System.FilePath
import Test.Hspec
import Test.Hspec.Core.Spec
deriving instance MonadCatch (SpecM a)
deriving instance MonadThrow (SpecM a)
deriving instance MonadMask (SpecM a)
deriving instance MonadIO (SpecM a)
hasExtension :: Text -> F.FilePath -> Bool
hasExtension = flip F.hasExtension
mapS :: (a -> SpecM () ()) -> Proxy () a y' y (SafeT (SpecM ())) r
mapS = P.mapM_ . (lift .)
paths :: MonadSafe m => String -> (F.FilePath -> Bool) -> Producer String m ()
paths dir p = every (childOf path) >-> P.filter p >-> P.map F.encodeString
where path = F.decodeString dir
-- | Helper function to generate a spec. The spec runs on the given directory,
-- filtering by the given function. It then compares their output to the text of
-- the file with @.out@ as the new extension.
--
-- As an example, consider the directory structure
--
-- > test/data
-- > ├── file.hs
-- > └── file.out
--
-- If we have a function called @formatFile@ and we run
--
-- > testFiles "test/data" (hasExtension "hs") formatFile
--
-- This would read @test\/data\/file.hs@, format the file if it can, and compare
-- the output to the contents of @test\/data\/file.out@.
testFiles :: (Eq a, Show a)
=> FilePath -- ^ Base directory
-> (F.FilePath -> Bool) -- ^ Filter on file extensions
-> (String -> Either a String) -- ^ Function to process a file
-> SpecWith ()
testFiles dir p f = runSafeT $ runEffect $ paths dir p >-> mapS (testFile f)
testFilesIO :: FilePath -> (F.FilePath -> Bool) -> (String -> IO String) -> SpecWith ()
testFilesIO dir p f = runSafeT $ runEffect $ paths dir p >-> mapS (testFileIO f)
testFileIO :: (String -> IO String) -> String -> SpecWith ()
testFileIO fun f = it f $ do
sample <- readFile f
expected <- readFile (replaceExtension f ".out")
fun sample >>= (`shouldBe` expected)
testFile :: (Eq a, Show a) => (String -> Either a String) -> String -> SpecWith ()
testFile fun f = it f $ do
sample <- readFile f
expected <- readFile (replaceExtension f ".out")
fun sample `shouldBe` Right expected