{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}

-- | This module defines how to run a test suite
module Test.Syd.Runner.Asynchronous
  ( runSpecForestAsynchronously,
    runSpecForestInterleavedWithOutputAsynchronously,
  )
where

import Control.Concurrent.Async as Async
import Control.Concurrent.MVar
import Control.Concurrent.QSem
import Control.Concurrent.STM as STM
import Control.Exception
#if MIN_VERSION_mtl(2,3,0)
import Control.Monad (when)
#endif
import Control.Monad.Reader
import Data.Maybe
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import Data.Vector (Vector)
import qualified Data.Vector as V
import Data.Word
import GHC.Clock (getMonotonicTimeNSec)
import Test.QuickCheck.IO ()
import Test.Syd.HList
import Test.Syd.OptParse
import Test.Syd.Output
import Test.Syd.Run
import Test.Syd.Runner.Single
import Test.Syd.SpecDef
import Test.Syd.SpecForest
import Text.Colour

runSpecForestAsynchronously :: Settings -> Word -> TestForest '[] () -> IO ResultForest
runSpecForestAsynchronously :: Settings -> Word -> TestForest '[] () -> IO ResultForest
runSpecForestAsynchronously Settings
settings Word
nbThreads TestForest '[] ()
testForest = do
  HandleForest '[] ()
handleForest <- forall (a :: [*]) b. TestForest a b -> IO (HandleForest a b)
makeHandleForest TestForest '[] ()
testForest
  MVar ()
failFastVar <- forall a. IO (MVar a)
newEmptyMVar
  let runRunner :: IO ()
runRunner = Settings -> Word -> MVar () -> HandleForest '[] () -> IO ()
runner Settings
settings Word
nbThreads MVar ()
failFastVar HandleForest '[] ()
handleForest
      runPrinter :: IO ResultForest
runPrinter = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ MVar () -> HandleForest '[] () -> IO ResultForest
waiter MVar ()
failFastVar HandleForest '[] ()
handleForest
  ((), ResultForest
resultForest) <- forall a b. IO a -> IO b -> IO (a, b)
concurrently IO ()
runRunner IO ResultForest
runPrinter
  forall (f :: * -> *) a. Applicative f => a -> f a
pure ResultForest
resultForest

runSpecForestInterleavedWithOutputAsynchronously :: Settings -> Word -> TestForest '[] () -> IO (Timed ResultForest)
runSpecForestInterleavedWithOutputAsynchronously :: Settings -> Word -> TestForest '[] () -> IO (Timed ResultForest)
runSpecForestInterleavedWithOutputAsynchronously Settings
settings Word
nbThreads TestForest '[] ()
testForest = do
  HandleForest '[] ()
handleForest <- forall (a :: [*]) b. TestForest a b -> IO (HandleForest a b)
makeHandleForest TestForest '[] ()
testForest
  MVar ()
failFastVar <- forall a. IO (MVar a)
newEmptyMVar
  Word64
suiteBegin <- IO Word64
getMonotonicTimeNSec
  let runRunner :: IO ()
runRunner = Settings -> Word -> MVar () -> HandleForest '[] () -> IO ()
runner Settings
settings Word
nbThreads MVar ()
failFastVar HandleForest '[] ()
handleForest
      runPrinter :: IO (Timed ResultForest)
runPrinter = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ Settings
-> MVar ()
-> Word64
-> HandleForest '[] ()
-> IO (Timed ResultForest)
printer Settings
settings MVar ()
failFastVar Word64
suiteBegin HandleForest '[] ()
handleForest
  ((), Timed ResultForest
resultForest) <- forall a b. IO a -> IO b -> IO (a, b)
concurrently IO ()
runRunner IO (Timed ResultForest)
runPrinter
  forall (f :: * -> *) a. Applicative f => a -> f a
pure Timed ResultForest
resultForest

type HandleForest a b = SpecDefForest a b (MVar (Timed TestRunReport))

type HandleTree a b = SpecDefTree a b (MVar (Timed TestRunReport))

makeHandleForest :: TestForest a b -> IO (HandleForest a b)
makeHandleForest :: forall (a :: [*]) b. TestForest a b -> IO (HandleForest a b)
makeHandleForest = forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse forall a b. (a -> b) -> a -> b
$ \() -> forall a. IO (MVar a)
newEmptyMVar

type Job = Int -> IO ()

-- | Job queue for workers that can synchronise
data JobQueue = JobQueue
  { -- | Bounded channel for the jobs.
    -- We use a TBQueue because it's bounded and we can check if it's empty.
    JobQueue -> TBQueue Job
jobQueueTBQueue :: !(TBQueue Job),
    -- | One semaphore per worker, which needs to be awaited before the worker
    -- can start doing a job.
    JobQueue -> Vector QSem
jobQueueWorking :: !(Vector QSem)
  }

-- | Make a new job queue with a given number of workers and capacity
newJobQueue :: Word -> Word -> IO JobQueue
newJobQueue :: Word -> Word -> IO JobQueue
newJobQueue Word
nbWorkers Word
spots = do
  TBQueue Job
jobQueueTBQueue <- forall a. Natural -> IO (TBQueue a)
newTBQueueIO (forall a b. (Integral a, Num b) => a -> b
fromIntegral Word
spots)
  Vector QSem
jobQueueWorking <- forall (m :: * -> *) a. Monad m => Int -> m a -> m (Vector a)
V.replicateM (forall a b. (Integral a, Num b) => a -> b
fromIntegral Word
nbWorkers) (Int -> IO QSem
newQSem Int
1)
  forall (f :: * -> *) a. Applicative f => a -> f a
pure JobQueue {Vector QSem
TBQueue Job
jobQueueWorking :: Vector QSem
jobQueueTBQueue :: TBQueue Job
jobQueueWorking :: Vector QSem
jobQueueTBQueue :: TBQueue Job
..}

-- | Enqueue a job, block until that's possible.
enqueueJob :: JobQueue -> Job -> IO ()
enqueueJob :: JobQueue -> Job -> IO ()
enqueueJob JobQueue {Vector QSem
TBQueue Job
jobQueueWorking :: Vector QSem
jobQueueTBQueue :: TBQueue Job
jobQueueWorking :: JobQueue -> Vector QSem
jobQueueTBQueue :: JobQueue -> TBQueue Job
..} Job
job =
  forall a. STM a -> IO a
atomically forall a b. (a -> b) -> a -> b
$ forall a. TBQueue a -> a -> STM ()
writeTBQueue TBQueue Job
jobQueueTBQueue Job
job

-- | Dequeue a job.
dequeueJob :: JobQueue -> IO Job
dequeueJob :: JobQueue -> IO Job
dequeueJob JobQueue {Vector QSem
TBQueue Job
jobQueueWorking :: Vector QSem
jobQueueTBQueue :: TBQueue Job
jobQueueWorking :: JobQueue -> Vector QSem
jobQueueTBQueue :: JobQueue -> TBQueue Job
..} =
  forall a. STM a -> IO a
atomically forall a b. (a -> b) -> a -> b
$ forall a. TBQueue a -> STM a
readTBQueue TBQueue Job
jobQueueTBQueue

-- | Block until all workers are done (waiting to dequeue a job).
blockUntilDone :: JobQueue -> IO ()
blockUntilDone :: JobQueue -> IO ()
blockUntilDone JobQueue {Vector QSem
TBQueue Job
jobQueueWorking :: Vector QSem
jobQueueTBQueue :: TBQueue Job
jobQueueWorking :: JobQueue -> Vector QSem
jobQueueTBQueue :: JobQueue -> TBQueue Job
..} = do
  -- Wait until the queue is empty.
  forall a. STM a -> IO a
atomically forall a b. (a -> b) -> a -> b
$ forall a. TBQueue a -> STM Bool
isEmptyTBQueue TBQueue Job
jobQueueTBQueue forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Bool -> STM ()
STM.check
  -- No new work can be started now, because the queue is empty.
  -- That means that all workers are either waiting for another job or still
  -- doing a job.

  -- Wait for all workers to stop working.
  -- That means that they're all just done working now or waiting for another job.
  -- Both are fine.
  forall (m :: * -> *) a b. Monad m => Vector a -> (a -> m b) -> m ()
V.forM_ Vector QSem
jobQueueWorking QSem -> IO ()
waitQSem
  -- The workers are now all done and the queue is empty, and this is the only
  -- thread enqueueing jobs, so no work is happening.
  -- Release all the workers so they can work again after this function.
  forall (m :: * -> *) a b. Monad m => Vector a -> (a -> m b) -> m ()
V.forM_ Vector QSem
jobQueueWorking QSem -> IO ()
signalQSem

withJobQueueWorkers :: Word -> JobQueue -> IO a -> IO a
withJobQueueWorkers :: forall a. Word -> JobQueue -> IO a -> IO a
withJobQueueWorkers Word
nbWorkers JobQueue
jobQueue IO a
func =
  forall a b. IO a -> (Async a -> IO b) -> IO b
withAsync
    ( forall (t :: * -> *) a b.
Traversable t =>
(a -> IO b) -> t a -> IO (t b)
mapConcurrently
        (JobQueue -> Job
jobQueueWorker JobQueue
jobQueue)
        [Int
0 .. forall a b. (Integral a, Num b) => a -> b
fromIntegral Word
nbWorkers forall a. Num a => a -> a -> a
- Int
1]
    )
    (\Async [()]
_ -> IO a
func)

jobQueueWorker :: JobQueue -> Int -> IO ()
jobQueueWorker :: JobQueue -> Job
jobQueueWorker JobQueue
jobQueue Int
workerIx = do
  let workingSem :: QSem
workingSem = JobQueue -> Vector QSem
jobQueueWorking JobQueue
jobQueue forall a. Vector a -> Int -> a
V.! Int
workerIx
  forall (f :: * -> *) a b. Applicative f => f a -> f b
forever forall a b. (a -> b) -> a -> b
$ do
    Job
job <- JobQueue -> IO Job
dequeueJob JobQueue
jobQueue
    forall a b c. IO a -> IO b -> IO c -> IO c
bracket_
      (QSem -> IO ()
waitQSem QSem
workingSem)
      (QSem -> IO ()
signalQSem QSem
workingSem)
      (Job
job Int
workerIx)

-- The plan is as follows:
--
-- We have:
--
-- 1 runner thread that schedules jobs
-- 1 waiter/printer thread that waits for the jobs to be done and puts them in
--   the result forest.
-- n worker threads that run the jobs.
--
-- Any outer resource might need cleanup, so whenever the scheduler thread
-- finishes an outer-resource subtree, it must wait for all tasks until then to
-- be completed before running the cleanup action.
--
-- There might be an ungodly number of tests so, to keep memory usage
-- contained, we want to limit the number of jobs that the scheduler can put on
-- the queue.
--
-- Tests may be marked as sequential, in which case only one test may be
-- executing at a time.
--
--
-- 1. We use a job queue semaphore that holds the number of empty
--    spots left on the queue.
--    The scheduler must wait for one unit of the semaphore before
--    enqueuing a job.
--    Any dequeuing must signal this semaphore
--
-- 2. We use a global lock for any job marked as "sequential".
--
--
-- The runner goes through the test 'HandleForest' one by one, and:
--
-- 1. Tries to enqueue as many jobs as possible.
--    It's only allowed to enqueue a jobs if there is space left on
--    the queue as indicated by the job semaphore.
--
-- 2. Asks workers to wait after finishing what they were doing at the end of
--     an outer resource block.
runner :: Settings -> Word -> MVar () -> HandleForest '[] () -> IO ()
runner :: Settings -> Word -> MVar () -> HandleForest '[] () -> IO ()
runner Settings
settings Word
nbThreads MVar ()
failFastVar HandleForest '[] ()
handleForest = do
  let nbWorkers :: Word
nbWorkers = Word
nbThreads
  let nbSpacesOnTheJobQueue :: Word
nbSpacesOnTheJobQueue = Word
nbWorkers forall a. Num a => a -> a -> a
* Word
2
  JobQueue
jobQueue <- Word -> Word -> IO JobQueue
newJobQueue Word
nbWorkers Word
nbSpacesOnTheJobQueue

  forall a. Word -> JobQueue -> IO a -> IO a
withJobQueueWorkers Word
nbWorkers JobQueue
jobQueue forall a b. (a -> b) -> a -> b
$ do
    let waitForWorkersDone :: IO ()
        waitForWorkersDone :: IO ()
waitForWorkersDone = JobQueue -> IO ()
blockUntilDone JobQueue
jobQueue

    let goForest :: forall a. HandleForest a () -> R a ()
        goForest :: forall (a :: [*]). HandleForest a () -> R a ()
goForest = forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ forall (a :: [*]). HandleTree a () -> R a ()
goTree

        goTree :: forall a. HandleTree a () -> R a ()
        goTree :: forall (a :: [*]). HandleTree a () -> R a ()
goTree = \case
          DefSpecifyNode Text
_ TDef
  (ProgressReporter
   -> ((HList a -> () -> IO ()) -> IO ()) -> IO TestRunResult)
td MVar (Timed TestRunReport)
var -> do
            -- If the fail-fast var has been put, we stop enqueuing jobs.
            Maybe ()
mDoneEarly <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. MVar a -> IO (Maybe a)
tryReadMVar MVar ()
failFastVar
            case Maybe ()
mDoneEarly of
              Just () -> forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
              Maybe ()
Nothing -> do
                Env {Word
HList a
ExpectationMode
FlakinessMode
Parallelism
eExternalResources :: forall (externalResources :: [*]).
Env externalResources -> HList externalResources
eExpectationMode :: forall (externalResources :: [*]).
Env externalResources -> ExpectationMode
eFlakinessMode :: forall (externalResources :: [*]).
Env externalResources -> FlakinessMode
eRetries :: forall (externalResources :: [*]). Env externalResources -> Word
eParallelism :: forall (externalResources :: [*]).
Env externalResources -> Parallelism
eExternalResources :: HList a
eExpectationMode :: ExpectationMode
eFlakinessMode :: FlakinessMode
eRetries :: Word
eParallelism :: Parallelism
..} <- forall r (m :: * -> *). MonadReader r m => m r
ask

                forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ do
                  let runNow :: Int -> IO (Timed TestRunReport)
runNow Int
workerNr =
                        forall (m :: * -> *) a. MonadIO m => Int -> m a -> m (Timed a)
timeItT Int
workerNr forall a b. (a -> b) -> a -> b
$
                          forall (externalResources :: [*]) t.
ProgressReporter
-> HList externalResources
-> TDef
     (ProgressReporter
      -> ((HList externalResources -> () -> t) -> t) -> IO TestRunResult)
-> Word
-> FlakinessMode
-> ExpectationMode
-> IO TestRunReport
runSingleTestWithFlakinessMode
                            ProgressReporter
noProgressReporter
                            HList a
eExternalResources
                            TDef
  (ProgressReporter
   -> ((HList a -> () -> IO ()) -> IO ()) -> IO TestRunResult)
td
                            Word
eRetries
                            FlakinessMode
eFlakinessMode
                            ExpectationMode
eExpectationMode

                  let job :: Int -> IO ()
                      job :: Job
job Int
workerNr = do
                        -- Start the test
                        Timed TestRunReport
result <- Int -> IO (Timed TestRunReport)
runNow Int
workerNr

                        -- Put the result in the mvar
                        forall a. MVar a -> a -> IO ()
putMVar MVar (Timed TestRunReport)
var Timed TestRunReport
result

                        -- If we should fail fast, put the
                        -- fail-fast var so that no new
                        -- jobs are started by the
                        -- scheduler.
                        forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when
                          ( Settings -> Bool
settingFailFast Settings
settings
                              Bool -> Bool -> Bool
&& Settings -> TestRunReport -> Bool
testRunReportFailed Settings
settings (forall a. Timed a -> a
timedValue Timed TestRunReport
result)
                          )
                          forall a b. (a -> b) -> a -> b
$ do
                            forall a. MVar a -> a -> IO ()
putMVar MVar ()
failFastVar ()

                  -- When enqueuing a sequential job, make sure all workers are
                  -- done before and after.
                  -- It's not enough to just not have two tests running at the
                  -- same time, because they also need to be executed in order.
                  case Parallelism
eParallelism of
                    Parallelism
Sequential -> do
                      IO ()
waitForWorkersDone
                      Job
job Int
0
                    Parallelism
Parallel -> do
                      JobQueue -> Job -> IO ()
enqueueJob JobQueue
jobQueue Job
job
          DefPendingNode Text
_ Maybe Text
_ -> forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
          DefDescribeNode Text
_ SpecDefForest a () (MVar (Timed TestRunReport))
sdf -> forall (a :: [*]). HandleForest a () -> R a ()
goForest SpecDefForest a () (MVar (Timed TestRunReport))
sdf
          DefSetupNode IO ()
func SpecDefForest a () (MVar (Timed TestRunReport))
sdf -> do
            forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO ()
func
            forall (a :: [*]). HandleForest a () -> R a ()
goForest SpecDefForest a () (MVar (Timed TestRunReport))
sdf
          DefBeforeAllNode IO outer
func SpecDefForest (outer : a) () (MVar (Timed TestRunReport))
sdf -> do
            outer
b <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO outer
func
            forall r' r (m :: * -> *) a.
(r' -> r) -> ReaderT r m a -> ReaderT r' m a
withReaderT
              (\Env a
e -> Env a
e {eExternalResources :: HList (outer : a)
eExternalResources = forall e (l :: [*]). e -> HList l -> HList (e : l)
HCons outer
b (forall (externalResources :: [*]).
Env externalResources -> HList externalResources
eExternalResources Env a
e)})
              (forall (a :: [*]). HandleForest a () -> R a ()
goForest SpecDefForest (outer : a) () (MVar (Timed TestRunReport))
sdf)
          DefBeforeAllWithNode oldOuter -> IO newOuter
func SpecDefForest
  (newOuter : oldOuter : otherOuters) () (MVar (Timed TestRunReport))
sdf -> do
            Env (oldOuter : otherOuters)
e <- forall r (m :: * -> *). MonadReader r m => m r
ask
            let HCons oldOuter
e
x HList l
_ = forall (externalResources :: [*]).
Env externalResources -> HList externalResources
eExternalResources Env (oldOuter : otherOuters)
e
            newOuter
b <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ oldOuter -> IO newOuter
func oldOuter
x
            forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$
              forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT
                (forall (a :: [*]). HandleForest a () -> R a ()
goForest SpecDefForest
  (newOuter : oldOuter : otherOuters) () (MVar (Timed TestRunReport))
sdf)
                (Env (oldOuter : otherOuters)
e {eExternalResources :: HList (newOuter : oldOuter : otherOuters)
eExternalResources = forall e (l :: [*]). e -> HList l -> HList (e : l)
HCons newOuter
b (forall (externalResources :: [*]).
Env externalResources -> HList externalResources
eExternalResources Env (oldOuter : otherOuters)
e)})
          DefWrapNode IO () -> IO ()
func SpecDefForest a () (MVar (Timed TestRunReport))
sdf -> do
            Env a
e <- forall r (m :: * -> *). MonadReader r m => m r
ask
            forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$
              IO () -> IO ()
func forall a b. (a -> b) -> a -> b
$ do
                forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT (forall (a :: [*]). HandleForest a () -> R a ()
goForest SpecDefForest a () (MVar (Timed TestRunReport))
sdf) Env a
e
                IO ()
waitForWorkersDone
          DefAroundAllNode (outer -> IO ()) -> IO ()
func SpecDefForest (outer : a) () (MVar (Timed TestRunReport))
sdf -> do
            Env a
e <- forall r (m :: * -> *). MonadReader r m => m r
ask
            forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$
              (outer -> IO ()) -> IO ()
func
                ( \outer
b -> do
                    forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT
                      (forall (a :: [*]). HandleForest a () -> R a ()
goForest SpecDefForest (outer : a) () (MVar (Timed TestRunReport))
sdf)
                      (Env a
e {eExternalResources :: HList (outer : a)
eExternalResources = forall e (l :: [*]). e -> HList l -> HList (e : l)
HCons outer
b (forall (externalResources :: [*]).
Env externalResources -> HList externalResources
eExternalResources Env a
e)})
                    IO ()
waitForWorkersDone
                )
          DefAroundAllWithNode (newOuter -> IO ()) -> oldOuter -> IO ()
func SpecDefForest
  (newOuter : oldOuter : otherOuters) () (MVar (Timed TestRunReport))
sdf -> do
            Env (oldOuter : otherOuters)
e <- forall r (m :: * -> *). MonadReader r m => m r
ask
            let HCons oldOuter
e
x HList l
_ = forall (externalResources :: [*]).
Env externalResources -> HList externalResources
eExternalResources Env (oldOuter : otherOuters)
e
            forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$
              (newOuter -> IO ()) -> oldOuter -> IO ()
func
                ( \newOuter
b -> do
                    forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT
                      (forall (a :: [*]). HandleForest a () -> R a ()
goForest SpecDefForest
  (newOuter : oldOuter : otherOuters) () (MVar (Timed TestRunReport))
sdf)
                      (Env (oldOuter : otherOuters)
e {eExternalResources :: HList (newOuter : oldOuter : otherOuters)
eExternalResources = forall e (l :: [*]). e -> HList l -> HList (e : l)
HCons newOuter
b (forall (externalResources :: [*]).
Env externalResources -> HList externalResources
eExternalResources Env (oldOuter : otherOuters)
e)})
                    IO ()
waitForWorkersDone
                )
                oldOuter
x
          DefAfterAllNode HList a -> IO ()
func SpecDefForest a () (MVar (Timed TestRunReport))
sdf -> do
            Env a
e <- forall r (m :: * -> *). MonadReader r m => m r
ask
            forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$
              forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT (forall (a :: [*]). HandleForest a () -> R a ()
goForest SpecDefForest a () (MVar (Timed TestRunReport))
sdf) Env a
e
                forall a b. IO a -> IO b -> IO a
`finally` ( do
                              IO ()
waitForWorkersDone
                              HList a -> IO ()
func (forall (externalResources :: [*]).
Env externalResources -> HList externalResources
eExternalResources Env a
e)
                          )
          DefParallelismNode Parallelism
p' SpecDefForest a () (MVar (Timed TestRunReport))
sdf ->
            forall r' r (m :: * -> *) a.
(r' -> r) -> ReaderT r m a -> ReaderT r' m a
withReaderT
              (\Env a
e -> Env a
e {eParallelism :: Parallelism
eParallelism = Parallelism
p'})
              (forall (a :: [*]). HandleForest a () -> R a ()
goForest SpecDefForest a () (MVar (Timed TestRunReport))
sdf)
          DefRandomisationNode ExecutionOrderRandomisation
_ SpecDefForest a () (MVar (Timed TestRunReport))
sdf ->
            forall (a :: [*]). HandleForest a () -> R a ()
goForest SpecDefForest a () (MVar (Timed TestRunReport))
sdf -- Ignore, randomisation has already happened.
          DefRetriesNode Word -> Word
modRetries SpecDefForest a () (MVar (Timed TestRunReport))
sdf ->
            forall r' r (m :: * -> *) a.
(r' -> r) -> ReaderT r m a -> ReaderT r' m a
withReaderT
              (\Env a
e -> Env a
e {eRetries :: Word
eRetries = Word -> Word
modRetries (forall (externalResources :: [*]). Env externalResources -> Word
eRetries Env a
e)})
              (forall (a :: [*]). HandleForest a () -> R a ()
goForest SpecDefForest a () (MVar (Timed TestRunReport))
sdf)
          DefFlakinessNode FlakinessMode
fm SpecDefForest a () (MVar (Timed TestRunReport))
sdf ->
            forall r' r (m :: * -> *) a.
(r' -> r) -> ReaderT r m a -> ReaderT r' m a
withReaderT
              (\Env a
e -> Env a
e {eFlakinessMode :: FlakinessMode
eFlakinessMode = FlakinessMode
fm})
              (forall (a :: [*]). HandleForest a () -> R a ()
goForest SpecDefForest a () (MVar (Timed TestRunReport))
sdf)
          DefExpectationNode ExpectationMode
em SpecDefForest a () (MVar (Timed TestRunReport))
sdf ->
            forall r' r (m :: * -> *) a.
(r' -> r) -> ReaderT r m a -> ReaderT r' m a
withReaderT
              (\Env a
e -> Env a
e {eExpectationMode :: ExpectationMode
eExpectationMode = ExpectationMode
em})
              (forall (a :: [*]). HandleForest a () -> R a ()
goForest SpecDefForest a () (MVar (Timed TestRunReport))
sdf)

    forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT
      (forall (a :: [*]). HandleForest a () -> R a ()
goForest HandleForest '[] ()
handleForest)
      Env
        { eParallelism :: Parallelism
eParallelism = Parallelism
Parallel,
          eRetries :: Word
eRetries = Settings -> Word
settingRetries Settings
settings,
          eFlakinessMode :: FlakinessMode
eFlakinessMode = FlakinessMode
MayNotBeFlaky,
          eExpectationMode :: ExpectationMode
eExpectationMode = ExpectationMode
ExpectPassing,
          eExternalResources :: HList '[]
eExternalResources = HList '[]
HNil
        }
    IO ()
waitForWorkersDone -- Make sure all jobs are done before cancelling the runners.

type R a = ReaderT (Env a) IO

-- Not exported, on purpose.
data Env externalResources = Env
  { forall (externalResources :: [*]).
Env externalResources -> Parallelism
eParallelism :: !Parallelism,
    forall (externalResources :: [*]). Env externalResources -> Word
eRetries :: !Word,
    forall (externalResources :: [*]).
Env externalResources -> FlakinessMode
eFlakinessMode :: !FlakinessMode,
    forall (externalResources :: [*]).
Env externalResources -> ExpectationMode
eExpectationMode :: !ExpectationMode,
    forall (externalResources :: [*]).
Env externalResources -> HList externalResources
eExternalResources :: !(HList externalResources)
  }

printer :: Settings -> MVar () -> Word64 -> HandleForest '[] () -> IO (Timed ResultForest)
printer :: Settings
-> MVar ()
-> Word64
-> HandleForest '[] ()
-> IO (Timed ResultForest)
printer Settings
settings MVar ()
failFastVar Word64
suiteBegin HandleForest '[] ()
handleForest = do
  TerminalCapabilities
tc <- Settings -> IO TerminalCapabilities
deriveTerminalCapababilities Settings
settings

  let outputLine :: [Chunk] -> IO ()
      outputLine :: [Chunk] -> IO ()
outputLine [Chunk]
lineChunks = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ do
        TerminalCapabilities -> [Chunk] -> IO ()
putChunksLocaleWith TerminalCapabilities
tc [Chunk]
lineChunks
        Text -> IO ()
TIO.putStrLn Text
""

      treeWidth :: Int
      treeWidth :: Int
treeWidth = forall (a :: [*]) b c. SpecDefForest a b c -> Int
specForestWidth HandleForest '[] ()
handleForest

  let pad :: Int -> [Chunk] -> [Chunk]
      pad :: Int -> [Chunk] -> [Chunk]
pad Int
level = (Text -> Chunk
chunk (String -> Text
T.pack (forall a. Int -> a -> [a]
replicate (Int
paddingSize forall a. Num a => a -> a -> a
* Int
level) Char
' ')) forall a. a -> [a] -> [a]
:)

  let outputLineP :: [Chunk] -> P ()
      outputLineP :: [Chunk] -> P ()
outputLineP [Chunk]
line = do
        Int
level <- forall r (m :: * -> *). MonadReader r m => m r
ask
        forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ [Chunk] -> IO ()
outputLine forall a b. (a -> b) -> a -> b
$ Int -> [Chunk] -> [Chunk]
pad Int
level [Chunk]
line

      outputLinesP :: [[Chunk]] -> P ()
      outputLinesP :: [[Chunk]] -> P ()
outputLinesP = forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ [Chunk] -> P ()
outputLineP

  let goForest :: HandleForest a b -> P (Maybe ResultForest)
      goForest :: forall (a :: [*]) b. HandleForest a b -> P (Maybe ResultForest)
goForest HandleForest a b
hts = do
        ResultForest
rts <- forall a. [Maybe a] -> [a]
catMaybes forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM forall (a :: [*]) b. HandleTree a b -> P (Maybe ResultTree)
goTree HandleForest a b
hts
        forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ if forall (t :: * -> *) a. Foldable t => t a -> Bool
null ResultForest
rts then forall a. Maybe a
Nothing else forall a. a -> Maybe a
Just ResultForest
rts

      goTree :: HandleTree a b -> P (Maybe ResultTree)
      goTree :: forall (a :: [*]) b. HandleTree a b -> P (Maybe ResultTree)
goTree = \case
        DefSpecifyNode Text
t TDef
  (ProgressReporter
   -> ((HList a -> b -> IO ()) -> IO ()) -> IO TestRunResult)
td MVar (Timed TestRunReport)
var -> do
          Either () (Timed TestRunReport)
failFastOrResult <-
            forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$
              forall a b. IO a -> IO b -> IO (Either a b)
race
                (forall a. MVar a -> IO a
readMVar MVar ()
failFastVar)
                (forall a. MVar a -> IO a
takeMVar MVar (Timed TestRunReport)
var)
          case Either () (Timed TestRunReport)
failFastOrResult of
            Left () -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing
            Right Timed TestRunReport
result -> do
              let td' :: TDef (Timed TestRunReport)
td' = TDef
  (ProgressReporter
   -> ((HList a -> b -> IO ()) -> IO ()) -> IO TestRunResult)
td {testDefVal :: Timed TestRunReport
testDefVal = Timed TestRunReport
result}
              Int
level <- forall r (m :: * -> *). MonadReader r m => m r
ask
              [[Chunk]] -> P ()
outputLinesP forall a b. (a -> b) -> a -> b
$ Settings
-> Int -> Int -> Text -> TDef (Timed TestRunReport) -> [[Chunk]]
outputSpecifyLines Settings
settings Int
level Int
treeWidth Text
t TDef (Timed TestRunReport)
td'
              forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ forall a. Text -> a -> SpecTree a
SpecifyNode Text
t TDef (Timed TestRunReport)
td'
        DefPendingNode Text
t Maybe Text
mr -> do
          [[Chunk]] -> P ()
outputLinesP forall a b. (a -> b) -> a -> b
$ Text -> Maybe Text -> [[Chunk]]
outputPendingLines Text
t Maybe Text
mr
          forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ forall a. Text -> Maybe Text -> SpecTree a
PendingNode Text
t Maybe Text
mr
        DefDescribeNode Text
t SpecDefForest a b (MVar (Timed TestRunReport))
sf -> do
          Maybe ()
mDoneEarly <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. MVar a -> IO (Maybe a)
tryReadMVar MVar ()
failFastVar
          case Maybe ()
mDoneEarly of
            Just () -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing
            Maybe ()
Nothing -> do
              [Chunk] -> P ()
outputLineP forall a b. (a -> b) -> a -> b
$ Text -> [Chunk]
outputDescribeLine Text
t
              forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (forall a. Text -> SpecForest a -> SpecTree a
DescribeNode Text
t) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. P a -> P a
addLevel (forall (a :: [*]) b. HandleForest a b -> P (Maybe ResultForest)
goForest SpecDefForest a b (MVar (Timed TestRunReport))
sf)
        DefSetupNode IO ()
_ SpecDefForest a b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> P (Maybe ResultForest)
goForest SpecDefForest a b (MVar (Timed TestRunReport))
sdf
        DefBeforeAllNode IO outer
_ SpecDefForest (outer : a) b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> P (Maybe ResultForest)
goForest SpecDefForest (outer : a) b (MVar (Timed TestRunReport))
sdf
        DefBeforeAllWithNode oldOuter -> IO newOuter
_ SpecDefForest
  (newOuter : oldOuter : otherOuters) b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> P (Maybe ResultForest)
goForest SpecDefForest
  (newOuter : oldOuter : otherOuters) b (MVar (Timed TestRunReport))
sdf
        DefWrapNode IO () -> IO ()
_ SpecDefForest a b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> P (Maybe ResultForest)
goForest SpecDefForest a b (MVar (Timed TestRunReport))
sdf
        DefAroundAllNode (outer -> IO ()) -> IO ()
_ SpecDefForest (outer : a) b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> P (Maybe ResultForest)
goForest SpecDefForest (outer : a) b (MVar (Timed TestRunReport))
sdf
        DefAroundAllWithNode (newOuter -> IO ()) -> oldOuter -> IO ()
_ SpecDefForest
  (newOuter : oldOuter : otherOuters) b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> P (Maybe ResultForest)
goForest SpecDefForest
  (newOuter : oldOuter : otherOuters) b (MVar (Timed TestRunReport))
sdf
        DefAfterAllNode HList a -> IO ()
_ SpecDefForest a b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> P (Maybe ResultForest)
goForest SpecDefForest a b (MVar (Timed TestRunReport))
sdf
        DefParallelismNode Parallelism
_ SpecDefForest a b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> P (Maybe ResultForest)
goForest SpecDefForest a b (MVar (Timed TestRunReport))
sdf
        DefRandomisationNode ExecutionOrderRandomisation
_ SpecDefForest a b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> P (Maybe ResultForest)
goForest SpecDefForest a b (MVar (Timed TestRunReport))
sdf
        DefRetriesNode Word -> Word
_ SpecDefForest a b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> P (Maybe ResultForest)
goForest SpecDefForest a b (MVar (Timed TestRunReport))
sdf
        DefFlakinessNode FlakinessMode
_ SpecDefForest a b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> P (Maybe ResultForest)
goForest SpecDefForest a b (MVar (Timed TestRunReport))
sdf
        DefExpectationNode ExpectationMode
_ SpecDefForest a b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> P (Maybe ResultForest)
goForest SpecDefForest a b (MVar (Timed TestRunReport))
sdf
  forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ [Chunk] -> IO ()
outputLine [[Chunk]]
outputTestsHeader
  ResultForest
resultForest <- forall a. a -> Maybe a -> a
fromMaybe [] forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT (forall (a :: [*]) b. HandleForest a b -> P (Maybe ResultForest)
goForest HandleForest '[] ()
handleForest) Int
0
  [Chunk] -> IO ()
outputLine [Text -> Chunk
chunk Text
" "]
  forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ [Chunk] -> IO ()
outputLine forall a b. (a -> b) -> a -> b
$ Settings -> ResultForest -> [[Chunk]]
outputFailuresWithHeading Settings
settings ResultForest
resultForest
  [Chunk] -> IO ()
outputLine [Text -> Chunk
chunk Text
" "]
  Word64
suiteEnd <- IO Word64
getMonotonicTimeNSec
  let timedResult :: Timed ResultForest
timedResult =
        Timed
          { timedValue :: ResultForest
timedValue = ResultForest
resultForest,
            timedWorker :: Int
timedWorker = Int
0,
            timedBegin :: Word64
timedBegin = Word64
suiteBegin,
            timedEnd :: Word64
timedEnd = Word64
suiteEnd
          }
  forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ [Chunk] -> IO ()
outputLine forall a b. (a -> b) -> a -> b
$ Timed TestSuiteStats -> [[Chunk]]
outputStats (Settings -> ResultForest -> TestSuiteStats
computeTestSuiteStats Settings
settings forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Timed ResultForest
timedResult)
  [Chunk] -> IO ()
outputLine [Text -> Chunk
chunk Text
" "]

  forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Settings -> Bool
settingProfile Settings
settings) forall a b. (a -> b) -> a -> b
$ do
    forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ [Chunk] -> IO ()
outputLine (Timed ResultForest -> [[Chunk]]
outputProfilingInfo Timed ResultForest
timedResult)
    [Chunk] -> IO ()
outputLine [Text -> Chunk
chunk Text
" "]

  forall (f :: * -> *) a. Applicative f => a -> f a
pure Timed ResultForest
timedResult

addLevel :: P a -> P a
addLevel :: forall a. P a -> P a
addLevel = forall r' r (m :: * -> *) a.
(r' -> r) -> ReaderT r m a -> ReaderT r' m a
withReaderT forall a. Enum a => a -> a
succ

type P = ReaderT Int IO

waiter :: MVar () -> HandleForest '[] () -> IO ResultForest
waiter :: MVar () -> HandleForest '[] () -> IO ResultForest
waiter MVar ()
failFastVar HandleForest '[] ()
handleForest = do
  let goForest :: HandleForest a b -> IO (Maybe ResultForest)
      goForest :: forall (a :: [*]) b. HandleForest a b -> IO (Maybe ResultForest)
goForest HandleForest a b
hts = do
        ResultForest
rts <- forall a. [Maybe a] -> [a]
catMaybes forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM forall (a :: [*]) b. HandleTree a b -> IO (Maybe ResultTree)
goTree HandleForest a b
hts
        forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ if forall (t :: * -> *) a. Foldable t => t a -> Bool
null ResultForest
rts then forall a. Maybe a
Nothing else forall a. a -> Maybe a
Just ResultForest
rts

      goTree :: HandleTree a b -> IO (Maybe ResultTree)
      goTree :: forall (a :: [*]) b. HandleTree a b -> IO (Maybe ResultTree)
goTree = \case
        DefSpecifyNode Text
t TDef
  (ProgressReporter
   -> ((HList a -> b -> IO ()) -> IO ()) -> IO TestRunResult)
td MVar (Timed TestRunReport)
var -> do
          Either () (Timed TestRunReport)
failFastOrResult <-
            forall a b. IO a -> IO b -> IO (Either a b)
race
              (forall a. MVar a -> IO a
readMVar MVar ()
failFastVar)
              (forall a. MVar a -> IO a
takeMVar MVar (Timed TestRunReport)
var)
          case Either () (Timed TestRunReport)
failFastOrResult of
            Left () -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing
            Right Timed TestRunReport
result -> do
              let td' :: TDef (Timed TestRunReport)
td' = TDef
  (ProgressReporter
   -> ((HList a -> b -> IO ()) -> IO ()) -> IO TestRunResult)
td {testDefVal :: Timed TestRunReport
testDefVal = Timed TestRunReport
result}
              forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ forall a. Text -> a -> SpecTree a
SpecifyNode Text
t TDef (Timed TestRunReport)
td'
        DefPendingNode Text
t Maybe Text
mr -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ forall a. Text -> Maybe Text -> SpecTree a
PendingNode Text
t Maybe Text
mr
        DefDescribeNode Text
t SpecDefForest a b (MVar (Timed TestRunReport))
sf -> do
          forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (forall a. Text -> SpecForest a -> SpecTree a
DescribeNode Text
t) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> IO (Maybe ResultForest)
goForest SpecDefForest a b (MVar (Timed TestRunReport))
sf
        DefSetupNode IO ()
_ SpecDefForest a b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> IO (Maybe ResultForest)
goForest SpecDefForest a b (MVar (Timed TestRunReport))
sdf
        DefBeforeAllNode IO outer
_ SpecDefForest (outer : a) b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> IO (Maybe ResultForest)
goForest SpecDefForest (outer : a) b (MVar (Timed TestRunReport))
sdf
        DefBeforeAllWithNode oldOuter -> IO newOuter
_ SpecDefForest
  (newOuter : oldOuter : otherOuters) b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> IO (Maybe ResultForest)
goForest SpecDefForest
  (newOuter : oldOuter : otherOuters) b (MVar (Timed TestRunReport))
sdf
        DefWrapNode IO () -> IO ()
_ SpecDefForest a b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> IO (Maybe ResultForest)
goForest SpecDefForest a b (MVar (Timed TestRunReport))
sdf
        DefAroundAllNode (outer -> IO ()) -> IO ()
_ SpecDefForest (outer : a) b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> IO (Maybe ResultForest)
goForest SpecDefForest (outer : a) b (MVar (Timed TestRunReport))
sdf
        DefAroundAllWithNode (newOuter -> IO ()) -> oldOuter -> IO ()
_ SpecDefForest
  (newOuter : oldOuter : otherOuters) b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> IO (Maybe ResultForest)
goForest SpecDefForest
  (newOuter : oldOuter : otherOuters) b (MVar (Timed TestRunReport))
sdf
        DefAfterAllNode HList a -> IO ()
_ SpecDefForest a b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> IO (Maybe ResultForest)
goForest SpecDefForest a b (MVar (Timed TestRunReport))
sdf
        DefParallelismNode Parallelism
_ SpecDefForest a b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> IO (Maybe ResultForest)
goForest SpecDefForest a b (MVar (Timed TestRunReport))
sdf
        DefRandomisationNode ExecutionOrderRandomisation
_ SpecDefForest a b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> IO (Maybe ResultForest)
goForest SpecDefForest a b (MVar (Timed TestRunReport))
sdf
        DefRetriesNode Word -> Word
_ SpecDefForest a b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> IO (Maybe ResultForest)
goForest SpecDefForest a b (MVar (Timed TestRunReport))
sdf
        DefFlakinessNode FlakinessMode
_ SpecDefForest a b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> IO (Maybe ResultForest)
goForest SpecDefForest a b (MVar (Timed TestRunReport))
sdf
        DefExpectationNode ExpectationMode
_ SpecDefForest a b (MVar (Timed TestRunReport))
sdf -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. SpecForest a -> SpecTree a
SubForestNode forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> IO (Maybe ResultForest)
goForest SpecDefForest a b (MVar (Timed TestRunReport))
sdf
  forall a. a -> Maybe a -> a
fromMaybe [] forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (a :: [*]) b. HandleForest a b -> IO (Maybe ResultForest)
goForest HandleForest '[] ()
handleForest