{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE IncoherentInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}

-- | This module defines all the functions you will use to define your test suite.
module Test.Syd.Def.AroundAll where

import Control.Monad.RWS.Strict
import Test.QuickCheck.IO ()
import Test.Syd.Def.TestDefM
import Test.Syd.HList
import Test.Syd.SpecDef

-- | Run a custom action before all spec items in a group, to set up an outer resource 'a'.
beforeAll :: IO a -> TestDefM (a ': l) c e -> TestDefM l c e
beforeAll :: IO a -> TestDefM (a : l) c e -> TestDefM l c e
beforeAll IO a
action = (TestForest (a : l) c -> TestTree l c)
-> TestDefM (a : l) c e -> TestDefM l c e
forall (a :: [*]) c (b :: [*]) d l.
(TestForest a c -> TestTree b d)
-> TestDefM a c l -> TestDefM b d l
wrapRWST ((TestForest (a : l) c -> TestTree l c)
 -> TestDefM (a : l) c e -> TestDefM l c e)
-> (TestForest (a : l) c -> TestTree l c)
-> TestDefM (a : l) c e
-> TestDefM l c e
forall a b. (a -> b) -> a -> b
$ \TestForest (a : l) c
forest -> IO a -> TestForest (a : l) c -> TestTree l c
forall a (l :: [*]) c e.
IO a -> SpecDefForest (a : l) c e -> SpecDefTree l c e
DefBeforeAllNode IO a
action TestForest (a : l) c
forest

-- | Run a custom action before all spec items in a group without setting up any outer resources.
beforeAll_ :: IO () -> TestDefM a b e -> TestDefM a b e
beforeAll_ :: IO () -> TestDefM a b e -> TestDefM a b e
beforeAll_ IO ()
action = (IO () -> IO ()) -> TestDefM a b e -> TestDefM a b e
forall (a :: [*]) b e.
(IO () -> IO ()) -> TestDefM a b e -> TestDefM a b e
aroundAll_ (IO ()
action IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>>)

-- | Run a custom action before all spec items in a group, to set up an outer resource 'b' by using the outer resource 'a'.
beforeAllWith :: (b -> IO a) -> TestDefM (a ': b ': l) c e -> TestDefM (b ': l) c e
beforeAllWith :: (b -> IO a) -> TestDefM (a : b : l) c e -> TestDefM (b : l) c e
beforeAllWith b -> IO a
action = ((a -> IO ()) -> b -> IO ())
-> TestDefM (a : b : l) c e -> TestDefM (b : l) c e
forall a b c (l :: [*]) r.
((a -> IO ()) -> b -> IO ())
-> TestDefM (a : b : l) c r -> TestDefM (b : l) c r
aroundAllWith (((a -> IO ()) -> b -> IO ())
 -> TestDefM (a : b : l) c e -> TestDefM (b : l) c e)
-> ((a -> IO ()) -> b -> IO ())
-> TestDefM (a : b : l) c e
-> TestDefM (b : l) c e
forall a b. (a -> b) -> a -> b
$ \a -> IO ()
func b
b -> do
  a
a <- b -> IO a
action b
b
  a -> IO ()
func a
a

-- | Run a custom action after all spec items, using the outer resource 'a'.
afterAll :: (a -> IO ()) -> TestDefM (a ': l) b e -> TestDefM (a ': l) b e
afterAll :: (a -> IO ()) -> TestDefM (a : l) b e -> TestDefM (a : l) b e
afterAll a -> IO ()
func = (HList (a : l) -> IO ())
-> TestDefM (a : l) b e -> TestDefM (a : l) b e
forall (l :: [*]) b e.
(HList l -> IO ()) -> TestDefM l b e -> TestDefM l b e
afterAll' ((HList (a : l) -> IO ())
 -> TestDefM (a : l) b e -> TestDefM (a : l) b e)
-> (HList (a : l) -> IO ())
-> TestDefM (a : l) b e
-> TestDefM (a : l) b e
forall a b. (a -> b) -> a -> b
$ \(HCons e
a HList l
_) -> a -> IO ()
func a
e
a

-- | Run a custom action after all spec items, using all the outer resources.
afterAll' :: (HList l -> IO ()) -> TestDefM l b e -> TestDefM l b e
afterAll' :: (HList l -> IO ()) -> TestDefM l b e -> TestDefM l b e
afterAll' HList l -> IO ()
func = (TestForest l b -> TestTree l b)
-> TestDefM l b e -> TestDefM l b e
forall (a :: [*]) c (b :: [*]) d l.
(TestForest a c -> TestTree b d)
-> TestDefM a c l -> TestDefM b d l
wrapRWST ((TestForest l b -> TestTree l b)
 -> TestDefM l b e -> TestDefM l b e)
-> (TestForest l b -> TestTree l b)
-> TestDefM l b e
-> TestDefM l b e
forall a b. (a -> b) -> a -> b
$ \TestForest l b
forest -> (HList l -> IO ()) -> TestForest l b -> TestTree l b
forall (a :: [*]) c e.
(HList a -> IO ()) -> SpecDefForest a c e -> SpecDefTree a c e
DefAfterAllNode HList l -> IO ()
func TestForest l b
forest

-- | Run a custom action after all spec items without using any outer resources.
afterAll_ :: IO () -> TestDefM a b e -> TestDefM a b e
afterAll_ :: IO () -> TestDefM a b e -> TestDefM a b e
afterAll_ IO ()
action = (HList a -> IO ()) -> TestDefM a b e -> TestDefM a b e
forall (l :: [*]) b e.
(HList l -> IO ()) -> TestDefM l b e -> TestDefM l b e
afterAll' ((HList a -> IO ()) -> TestDefM a b e -> TestDefM a b e)
-> (HList a -> IO ()) -> TestDefM a b e -> TestDefM a b e
forall a b. (a -> b) -> a -> b
$ \HList a
_ -> IO ()
action

-- | Run a custom action before and/or after all spec items in group, to provide access to a resource 'a'.
--
-- See the @FOOTGUN@ note in the docs for 'around_'.
aroundAll :: ((a -> IO ()) -> IO ()) -> TestDefM (a ': l) c e -> TestDefM l c e
aroundAll :: ((a -> IO ()) -> IO ()) -> TestDefM (a : l) c e -> TestDefM l c e
aroundAll (a -> IO ()) -> IO ()
func = (TestForest (a : l) c -> TestTree l c)
-> TestDefM (a : l) c e -> TestDefM l c e
forall (a :: [*]) c (b :: [*]) d l.
(TestForest a c -> TestTree b d)
-> TestDefM a c l -> TestDefM b d l
wrapRWST ((TestForest (a : l) c -> TestTree l c)
 -> TestDefM (a : l) c e -> TestDefM l c e)
-> (TestForest (a : l) c -> TestTree l c)
-> TestDefM (a : l) c e
-> TestDefM l c e
forall a b. (a -> b) -> a -> b
$ \TestForest (a : l) c
forest -> ((a -> IO ()) -> IO ()) -> TestForest (a : l) c -> TestTree l c
forall a (l :: [*]) c e.
((a -> IO ()) -> IO ())
-> SpecDefForest (a : l) c e -> SpecDefTree l c e
DefAroundAllNode (a -> IO ()) -> IO ()
func TestForest (a : l) c
forest

-- | Run a custom action before and/or after all spec items in a group without accessing any resources.
--
-- == __FOOTGUN__
--
-- This combinator gives the programmer a lot of power.
-- In fact, it gives the programmer enough power to break the test framework.
-- Indeed, you can provide a wrapper function that just _doesn't_ run the function like this:
--
-- > spec :: Spec
-- > spec = do
-- >    let don'tDo :: IO () -> IO ()
-- >        don'tDo _ = pure ()
-- >    aroundAll_ don'tDo $ do
-- >      it "should pass" True
--
-- During execution, you'll then get an error like this:
--
-- > thread blocked indefinitely in an MVar operation
--
-- The same problem exists when using 'Test.Syd.Def.Around.around_'.
--
-- Something even more pernicious goes wrong when you run the given action more than once like this:
--
-- > spec :: Spec
-- > spec = do
-- >    let doTwice :: IO () -> IO ()
-- >        doTwice f = f >> f
-- >    aroundAll_ doTwice $ do
-- >      it "should pass" True
--
-- In this case, the test will "just work", but it will be executed twice even if the output reports that it only passed once.
--
-- Note: If you're interested in fixing this, talk to me, but only after GHC has gotten impredicative types because that will likely be a requirement.
aroundAll_ :: (IO () -> IO ()) -> TestDefM a b e -> TestDefM a b e
aroundAll_ :: (IO () -> IO ()) -> TestDefM a b e -> TestDefM a b e
aroundAll_ IO () -> IO ()
func = (TestForest a b -> TestTree a b)
-> TestDefM a b e -> TestDefM a b e
forall (a :: [*]) c (b :: [*]) d l.
(TestForest a c -> TestTree b d)
-> TestDefM a c l -> TestDefM b d l
wrapRWST ((TestForest a b -> TestTree a b)
 -> TestDefM a b e -> TestDefM a b e)
-> (TestForest a b -> TestTree a b)
-> TestDefM a b e
-> TestDefM a b e
forall a b. (a -> b) -> a -> b
$ \TestForest a b
forest -> (IO () -> IO ()) -> TestForest a b -> TestTree a b
forall (a :: [*]) c e.
(IO () -> IO ()) -> SpecDefForest a c e -> SpecDefTree a c e
DefWrapNode IO () -> IO ()
func TestForest a b
forest

-- | Run a custom action before and/or after all spec items in a group to provide access to a resource 'a' while using a resource 'b'
--
-- See the @FOOTGUN@ note in the docs for 'around_'.
aroundAllWith ::
  forall a b c l r.
  ((a -> IO ()) -> (b -> IO ())) ->
  TestDefM (a ': b ': l) c r ->
  TestDefM (b ': l) c r
aroundAllWith :: ((a -> IO ()) -> b -> IO ())
-> TestDefM (a : b : l) c r -> TestDefM (b : l) c r
aroundAllWith (a -> IO ()) -> b -> IO ()
func = (TestForest (a : b : l) c -> TestTree (b : l) c)
-> TestDefM (a : b : l) c r -> TestDefM (b : l) c r
forall (a :: [*]) c (b :: [*]) d l.
(TestForest a c -> TestTree b d)
-> TestDefM a c l -> TestDefM b d l
wrapRWST ((TestForest (a : b : l) c -> TestTree (b : l) c)
 -> TestDefM (a : b : l) c r -> TestDefM (b : l) c r)
-> (TestForest (a : b : l) c -> TestTree (b : l) c)
-> TestDefM (a : b : l) c r
-> TestDefM (b : l) c r
forall a b. (a -> b) -> a -> b
$ \TestForest (a : b : l) c
forest -> ((a -> IO ()) -> b -> IO ())
-> TestForest (a : b : l) c -> TestTree (b : l) c
forall b a (l :: [*]) c e.
((b -> IO ()) -> a -> IO ())
-> SpecDefForest (b : a : l) c e -> SpecDefTree (a : l) c e
DefAroundAllWithNode (a -> IO ()) -> b -> IO ()
func TestForest (a : b : l) c
forest

-- | Declare a node in the spec def forest
wrapRWST :: (TestForest a c -> TestTree b d) -> TestDefM a c l -> TestDefM b d l
wrapRWST :: (TestForest a c -> TestTree b d)
-> TestDefM a c l -> TestDefM b d l
wrapRWST TestForest a c -> TestTree b d
func (TestDefM RWST TestRunSettings (TestForest a c) () IO l
rwst) = RWST TestRunSettings (TestForest b d) () IO l -> TestDefM b d l
forall (a :: [*]) b c.
RWST TestRunSettings (TestForest a b) () IO c -> TestDefM a b c
TestDefM (RWST TestRunSettings (TestForest b d) () IO l -> TestDefM b d l)
-> RWST TestRunSettings (TestForest b d) () IO l -> TestDefM b d l
forall a b. (a -> b) -> a -> b
$
  ((IO (l, (), TestForest a c) -> IO (l, (), TestForest b d))
 -> RWST TestRunSettings (TestForest a c) () IO l
 -> RWST TestRunSettings (TestForest b d) () IO l)
-> RWST TestRunSettings (TestForest a c) () IO l
-> (IO (l, (), TestForest a c) -> IO (l, (), TestForest b d))
-> RWST TestRunSettings (TestForest b d) () IO l
forall a b c. (a -> b -> c) -> b -> a -> c
flip (IO (l, (), TestForest a c) -> IO (l, (), TestForest b d))
-> RWST TestRunSettings (TestForest a c) () IO l
-> RWST TestRunSettings (TestForest b d) () IO l
forall (m :: * -> *) a s w (n :: * -> *) b w' r.
(m (a, s, w) -> n (b, s, w')) -> RWST r w s m a -> RWST r w' s n b
mapRWST RWST TestRunSettings (TestForest a c) () IO l
rwst ((IO (l, (), TestForest a c) -> IO (l, (), TestForest b d))
 -> RWST TestRunSettings (TestForest b d) () IO l)
-> (IO (l, (), TestForest a c) -> IO (l, (), TestForest b d))
-> RWST TestRunSettings (TestForest b d) () IO l
forall a b. (a -> b) -> a -> b
$ \IO (l, (), TestForest a c)
inner -> do
    (l
res, ()
s, TestForest a c
forest) <- IO (l, (), TestForest a c)
inner
    let forest' :: TestForest b d
forest' = [TestForest a c -> TestTree b d
func TestForest a c
forest]
    (l, (), TestForest b d) -> IO (l, (), TestForest b d)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (l
res, ()
s, TestForest b d
forest')