module Test.Tasty.Runners.TAP
( tapRunner
, tapRunner'
) where
import Control.Concurrent.STM
import Control.Monad
import qualified Data.IntMap.Strict as IM
import Data.List
import Data.Maybe
import Data.Monoid
import System.IO
import Test.Tasty.Ingredients
import Test.Tasty.Options
import Test.Tasty.Runners
tapRunner :: Ingredient
tapRunner = tapRunner' stdout
tapRunner' :: Handle -> Ingredient
tapRunner' h = TestReporter reporterOpts go
where reporterOpts = []
go opts tree = (Just (tapCallback h opts tree))
tapCallback
:: Handle
-> OptionSet
-> TestTree
-> StatusMap
-> IO (Time -> IO Bool)
tapCallback h opts tree smap = do
let names = IM.fromList (zip [0..] (testsNames opts tree))
hPutStrLn h tapHeader
hPutStrLn h (tapPlan smap)
results <- forM (IM.toList smap) $ \(idx, stat) -> do
let n = fromMaybe mempty (IM.lookup idx names)
reportStatus h n idx stat
return (const (return (and results)))
tapHeader :: String
tapHeader = "TAP version 13"
tapPlan :: StatusMap -> String
tapPlan sm | IM.null sm = "Bail out! There were no tests to run."
| otherwise = "1.." <> show (IM.size sm)
reportStatus :: Handle -> String -> Int -> TVar Status -> IO Bool
reportStatus h name zeroIdx statRef = do
res <- atomically (waitFinished )
hPutStrLn h (renderResult res)
return (resultSuccessful res)
where testNum = zeroIdx + 1
waitFinished = do stat <- readTVar statRef
case stat of
Done res -> return res
_ -> retry
renderResult res
| resultSuccessful res = "ok " <> show testNum <> " - " <> name <> desc
| otherwise = "not ok " <> show testNum <> " - " <> name <> desc
where desc = case resultDescription res of
"" -> ""
nonEmpty -> "\n" <> formatDesc nonEmpty
formatDesc :: String -> String
formatDesc = intercalate "\n" . map ("# " <> ) . lines