module Test.Tasty.Silver.Interactive
(
defaultMain
, interactiveTests
, Interactive (..)
, runTestsInteractive
)
where
import Test.Tasty hiding (defaultMain)
import Test.Tasty.Runners
import Test.Tasty.Options
import Test.Tasty.Silver.Internal
import Data.Typeable
import Data.Tagged
import Data.Proxy
import Data.Maybe
import Control.Monad.Cont
import Control.Monad.State
import Text.Printf
import qualified Data.Text as T
import Data.Text.Encoding
import Options.Applicative
import System.Process.ByteString.Lazy as PL
import System.Process
import qualified Data.ByteString.Lazy as B
import qualified Data.ByteString as BS
import System.IO
import System.IO.Temp
import System.FilePath
defaultMain :: TestTree -> IO ()
defaultMain = defaultMainWithIngredients [interactiveTests, listingTests, consoleTestReporter]
newtype Interactive = Interactive Bool
deriving (Eq, Ord, Typeable)
instance IsOption Interactive where
defaultValue = Interactive False
parseValue = fmap Interactive . safeRead
optionName = return "interactive"
optionHelp = return "Run tests in interactive mode."
optionCLParser =
fmap Interactive $
switch
( long (untag (optionName :: Tagged Interactive String))
<> help (untag (optionHelp :: Tagged Interactive String))
)
interactiveTests :: Ingredient
interactiveTests = TestManager [Option (Proxy :: Proxy Interactive)] $
\opts tree ->
case lookupOption opts of
Interactive False -> Nothing
Interactive True -> Just $
runTestsInteractive opts tree
getGoldenTests :: OptionSet -> TestTree -> [(TestName, Golden)]
getGoldenTests =
foldTestTree
trivialFold { foldSingle = \_ name t -> fmap ((,) name) $ maybeToList $ cast t }
runTestsInteractive :: OptionSet -> TestTree -> IO Bool
runTestsInteractive opts tests = do
let gs = getGoldenTests opts tests
liftIO $ hSetBuffering stdout NoBuffering
(nFail, nReject) <- flip execStateT (0, 0) $ forM_ gs runTest
when (nFail > 0 || nReject > 0) (do
_ <- printf "NOTE: %d tests threw exceptions!\n" nFail
printf "NOTE: %d tests were rejected!\n" nReject
)
return (nFail == 0 && nReject == 0)
where runTest :: (TestName, Golden) -> StateT (Integer, Integer) IO ()
runTest (n, (Golden getGolden getActual cmp shw upd)) = do
liftIO $ putStrLn "Executing test"
(pFail, pReject, act) <- liftIO $ do
tested <- getActual
liftIO $ putStrLn "Getting golden"
golden <- getGolden
case golden of
Nothing -> do
_ <- liftIO $ printf "%s: No golden value. Press <enter> to see actual value.\n" n
_ <- liftIO getLine
_ <- liftIO $ shw tested >>= showValue n
liftIO $ tryAccept n upd tested
Just golden' -> do
cmp' <- liftIO $ cmp golden' tested
case cmp' of
Equal -> do
_ <- liftIO $ printf "%s: Golden value matches output.\n" n
return (0, 0, return ())
diff' -> do
_ <- liftIO $ printf "%s: Output does not match golden value. Press <enter> to see diff.\n" n
_ <- liftIO getLine
_ <- liftIO $ showDiff n diff'
liftIO $ tryAccept n upd tested
liftIO act
modify (\(nFail, nReject) -> (nFail + pFail, nReject + pReject))
tryAccept :: TestName -> (a -> IO ()) -> a -> IO (Integer, Integer, IO ())
tryAccept n upd new = do
_ <- printf "%s: Accept actual value as new golden value? [yn]" n
ans <- getLine
case ans of
"y" -> do
return (0, 0, upd new)
"n" -> return (0, 1, return ())
_ -> do
putStrLn "Invalid answer."
tryAccept n upd new
showDiff :: TestName -> GDiff -> IO ()
showDiff n (DiffText _ tGold tAct) = do
withSystemTempFile (n <.> "golden") (\fGold hGold -> do
withSystemTempFile (n <.> "actual") (\fAct hAct -> do
hSetBinaryMode hGold True
hSetBinaryMode hAct True
BS.hPut hGold (encodeUtf8 tGold)
BS.hPut hAct (encodeUtf8 tAct)
hClose hGold
hClose hAct
callProcess "sh"
["-c", "git diff --color=always --no-index --text " ++ fGold ++ " " ++ fAct ++ " | less -r > /dev/tty"]
)
)
showDiff n (ShowDiffed _ t) = showInLess n t
showDiff _ Equal = error "Can't show diff for equal values..."
showValue :: TestName -> GShow -> IO ()
showValue n (ShowText t) = showInLess n t
showInLess :: String -> T.Text -> IO ()
showInLess _ t = do
_ <- PL.readProcessWithExitCode "sh" ["-c", "less > /dev/tty"] inp
return ()
where inp = B.fromStrict $ encodeUtf8 t