packages feed

falsify-0.4.0: src/Test/Falsify/Driver.hs

-- | The main @falsify@ driver
--
-- The main entrypoint into @falsify@ is the 'falsify' function, which attempts
-- to falsify the t'Test.Falsify.Property'' it is given.
-- However, most users don't need to use this module directly; it is primarily
-- intended for integration of @falsify@ in test frameworks such as @tasty@.
-- For the case of @tasty@ /specifically/, see the @tasty-falsify@ package.
module Test.Falsify.Driver (
    falsify
    -- * Options
  , Options(..)
  , ReplaySeed(..)
  , ExpectFailure(..)
  , Verbose(..)
  , parseReplaySeed
    -- * Results
  , TestOutcome -- opaque
  , TestOutcome'
  , RenderedTestOutcome(..)
  , renderTestOutcome
    -- ** Additional accessors
  , successes
  , discarded
  , failure
  , failure'
  ) where

import Data.Bifunctor
import Data.List.NonEmpty (NonEmpty)

import qualified Data.List.NonEmpty as NE

import Test.Falsify.Internal.Driver
import Test.Falsify.Internal.Driver.ReplaySeed
import Test.Falsify.Internal.Property
import Test.Falsify.Internal.Shrinking

{-------------------------------------------------------------------------------
  Additional public accessors for 'TestOutcome'

  'TestOutcome' has quite a bit of @falsify@-internal detail; here we provide
  some accessors that may be useful in user code, without having to expose all
  of those internals.
-------------------------------------------------------------------------------}

-- | Successful test outcomes
--
-- NOTE: Normally the @a@ parameter for a top-level property is instantiated to
-- unit (@()@), in which case it's really only the /length/ of the list of
-- successes that is relevant.
successes :: forall e a. TestOutcome' e a -> [(ReplaySeed, a)]
successes TestOutcome{testSuccesses} = map aux testSuccesses
  where
    -- Drops the 'TestRun'
    aux :: Success a -> (ReplaySeed, a)
    aux Success{successSeed, successResult} = (successSeed, successResult)

-- | Number of discarded test
discarded :: TestOutcome' e a -> Word
discarded TestOutcome{testDiscarded} = testDiscarded

-- | Failing test, if any
--
-- Returns the error after shrinking
failure :: TestOutcome' e a -> Maybe (ReplaySeed, e)
failure = fmap (second NE.last) . failure'

-- | Generalization of 'failure' that returns the full shrinking history
failure' :: forall e a. TestOutcome' e a -> Maybe (ReplaySeed, NonEmpty e)
failure' TestOutcome{testFailure} = aux <$> testFailure
  where
    aux :: Failure e -> (ReplaySeed, NonEmpty e)
    aux Failure{failureSeed, failureRun} = (
          failureSeed
        ,   shrinkHistory
          $ first counterexampleError
          $ failureRun
        )