{-# LANGUAGE ScopedTypeVariables #-}

module Test.Syd.Runner.Single (runSingleTestWithFlakinessMode) where

import Data.List.NonEmpty (NonEmpty (..))
import qualified Data.List.NonEmpty as NE
import Test.Syd.HList
import Test.Syd.Run
import Test.Syd.SpecDef

-- | Run a single test.
--
-- Run the test up to 'maxRetries' times.
-- Finish as soon as the test passes once, or when we run out of retries.
runSingleTestWithFlakinessMode ::
  forall externalResources t.
  -- | How to report test progress
  ProgressReporter ->
  -- | External resources
  HList externalResources ->
  -- | Test definition
  TDef
    ( ProgressReporter ->
      ((HList externalResources -> () -> t) -> t) ->
      IO TestRunResult
    ) ->
  -- | Max retries
  Word ->
  -- | Flakiness mode
  FlakinessMode ->
  -- | Expectation mode
  ExpectationMode ->
  -- | Test result
  IO TestRunReport
runSingleTestWithFlakinessMode :: forall (externalResources :: [*]) t.
ProgressReporter
-> HList externalResources
-> TDef
     (ProgressReporter
      -> ((HList externalResources -> () -> t) -> t) -> IO TestRunResult)
-> Word
-> FlakinessMode
-> ExpectationMode
-> IO TestRunReport
runSingleTestWithFlakinessMode ProgressReporter
progressReporter HList externalResources
l TDef
  (ProgressReporter
   -> ((HList externalResources -> () -> t) -> t) -> IO TestRunResult)
td Word
maxRetries FlakinessMode
fm ExpectationMode
em = do
  NonEmpty TestRunResult
results <- forall (externalResources :: [*]) t.
ProgressReporter
-> HList externalResources
-> TDef
     (ProgressReporter
      -> ((HList externalResources -> () -> t) -> t) -> IO TestRunResult)
-> Word
-> ExpectationMode
-> IO (NonEmpty TestRunResult)
runSingleTestWithRetries ProgressReporter
progressReporter HList externalResources
l TDef
  (ProgressReporter
   -> ((HList externalResources -> () -> t) -> t) -> IO TestRunResult)
td Word
maxRetries ExpectationMode
em
  forall (f :: * -> *) a. Applicative f => a -> f a
pure
    TestRunReport
      { testRunReportExpectationMode :: ExpectationMode
testRunReportExpectationMode = ExpectationMode
em,
        testRunReportRawResults :: NonEmpty TestRunResult
testRunReportRawResults = NonEmpty TestRunResult
results,
        testRunReportFlakinessMode :: FlakinessMode
testRunReportFlakinessMode = FlakinessMode
fm
      }

runSingleTestWithRetries ::
  forall externalResources t.
  -- | How to report test progress
  ProgressReporter ->
  -- | External resources
  HList externalResources ->
  -- | Test definition
  TDef
    ( ProgressReporter ->
      ((HList externalResources -> () -> t) -> t) ->
      IO TestRunResult
    ) ->
  -- | Max retries
  Word ->
  -- | Expectation mode
  ExpectationMode ->
  -- If the test ever passed, and the last test result
  IO (NonEmpty TestRunResult)
runSingleTestWithRetries :: forall (externalResources :: [*]) t.
ProgressReporter
-> HList externalResources
-> TDef
     (ProgressReporter
      -> ((HList externalResources -> () -> t) -> t) -> IO TestRunResult)
-> Word
-> ExpectationMode
-> IO (NonEmpty TestRunResult)
runSingleTestWithRetries ProgressReporter
progressReporter HList externalResources
l TDef
  (ProgressReporter
   -> ((HList externalResources -> () -> t) -> t) -> IO TestRunResult)
td Word
maxRetries ExpectationMode
em = Word -> IO (NonEmpty TestRunResult)
go Word
maxRetries
  where
    go :: Word -> IO (NonEmpty TestRunResult)
    go :: Word -> IO (NonEmpty TestRunResult)
go Word
w
      | Word
w forall a. Ord a => a -> a -> Bool
<= Word
1 = (forall a. a -> [a] -> NonEmpty a
:| []) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO TestRunResult
runFunc
      | Bool
otherwise = do
        TestRunResult
result <- IO TestRunResult
runFunc
        if TestStatus -> ExpectationMode -> Bool
testStatusMatchesExpectationMode (TestRunResult -> TestStatus
testRunResultStatus TestRunResult
result) ExpectationMode
em
          then forall (f :: * -> *) a. Applicative f => a -> f a
pure (TestRunResult
result forall a. a -> [a] -> NonEmpty a
:| [])
          else do
            NonEmpty TestRunResult
rest <- Word -> IO (NonEmpty TestRunResult)
go (forall a. Enum a => a -> a
pred Word
w)
            forall (f :: * -> *) a. Applicative f => a -> f a
pure (TestRunResult
result forall a. a -> NonEmpty a -> NonEmpty a
NE.<| NonEmpty TestRunResult
rest)
      where
        runFunc :: IO TestRunResult
        runFunc :: IO TestRunResult
runFunc = forall value. TDef value -> value
testDefVal TDef
  (ProgressReporter
   -> ((HList externalResources -> () -> t) -> t) -> IO TestRunResult)
td ProgressReporter
progressReporter (\HList externalResources -> () -> t
f -> HList externalResources -> () -> t
f HList externalResources
l ())