{-# LANGUAGE CPP #-} {-# LANGUAGE RecordWildCards #-} module Session ( Session(..) , new , close , reload , Summary(..) , resetSummary , hspecPreviousSummary , isFailure , isSuccess , getRunSpec #ifdef TEST , runSpec , hasSpec , hspecFailureEnvName , hasHspecCommandSignature , hspecCommand , parseSummary #endif ) where import Data.IORef import Data.List.Compat import Data.Maybe (listToMaybe, catMaybes) import Prelude () import Prelude.Compat import Text.Read.Compat import qualified Language.Haskell.GhciWrapper as GhciWrapper import Language.Haskell.GhciWrapper hiding (new, close) import Util import Options hspecFailureEnvName :: String hspecFailureEnvName = "HSPEC_FAILURES" data Session = Session { sessionInterpreter :: Interpreter , sessionHspecArgs :: [String] , sessionHspecPreviousSummary :: IORef (Maybe Summary) } resetSummary :: Session -> IO () resetSummary Session{..} = writeIORef sessionHspecPreviousSummary (Just $ Summary 0 0) hspecPreviousSummary :: Session -> IO (Maybe Summary) hspecPreviousSummary Session{..} = readIORef sessionHspecPreviousSummary new :: [String] -> IO Session new args = do let (ghciArgs, hspecArgs) = splitArgs args ghci <- GhciWrapper.new defaultConfig{configVerbose = True, configIgnoreDotGhci = False} ghciArgs _ <- eval ghci (":set prompt " ++ show "") _ <- eval ghci ("import qualified System.Environment") _ <- eval ghci ("import qualified Test.Hspec.Runner") _ <- eval ghci ("import qualified Test.Hspec.Meta") _ <- eval ghci ("System.Environment.unsetEnv " ++ show hspecFailureEnvName) ref <- newIORef (Just $ Summary 0 0) return (Session ghci hspecArgs ref) close :: Session -> IO () close = GhciWrapper.close . sessionInterpreter reload :: Session -> IO String reload Session{..} = evalEcho sessionInterpreter ":reload" data Summary = Summary { summaryExamples :: Int , summaryFailures :: Int } deriving (Eq, Show, Read) hspecCommand :: String hspecCommand = "Test.Hspec.Runner.hspecResult spec" hspecMetaCommand :: String hspecMetaCommand = "Test.Hspec.Meta.hspecResult spec" getRunSpec :: Session -> IO (Maybe (IO String)) getRunSpec session = do r <- getRunSpecWith hspecCommand session case r of Just _ -> return r Nothing -> getRunSpecWith hspecMetaCommand session getRunSpecWith :: String -> Session -> IO (Maybe (IO String)) getRunSpecWith command session = do has <- hasSpec command session if has then return $ Just (runSpec command session) else return Nothing hasSpec :: String -> Session -> IO Bool hasSpec command Session{..} = hasHspecCommandSignature command <$> eval sessionInterpreter (":type " ++ command) hasHspecCommandSignature :: String -> String -> Bool hasHspecCommandSignature command = any match . lines . normalizeTypeSignatures where match line = (command ++ " :: IO ") `isPrefixOf` line && "Summary" `isSuffixOf` line runSpec :: String -> Session -> IO String runSpec command session@Session{..} = do failedPreviously <- isFailure <$> hspecPreviousSummary session let args = "--color" : (if failedPreviously then addRerun else id) sessionHspecArgs r <- evalEcho sessionInterpreter $ "System.Environment.withArgs " ++ show args ++ " $ " ++ command writeIORef sessionHspecPreviousSummary (parseSummary r) return r where addRerun :: [String] -> [String] addRerun args = "--rerun" : args isFailure :: Maybe Summary -> Bool isFailure = maybe True ((/= 0) . summaryFailures) isSuccess :: Maybe Summary -> Bool isSuccess = not . isFailure parseSummary :: String -> Maybe Summary parseSummary = findJust . map (readMaybe . dropAnsiEscapeSequences) . reverse . lines where findJust = listToMaybe . catMaybes dropAnsiEscapeSequences xs | "Summary" `isPrefixOf` xs = xs | otherwise = case xs of _ : ys -> dropAnsiEscapeSequences ys [] -> []