------------------------------------------------------------------------------- ------------------------------------------------------------------------------- {-# LANGUAGE DataKinds #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE Rank2Types #-} {-# LANGUAGE TemplateHaskell #-} -- | -- -- Module : Test.BDD.Language -- Copyright : (c) Paolo Veronelli, Pavlo Kerestey 2017 -- License : BSD3 -- Maintainer: paolo.veronelli@gmail.com -- Stability : experimental -- Portability: non-portable -- -- -- The constrained language to define behaviors in BDD terminology -- -- @ -- exampleL :: TestTree -- exampleL = testBehavior "Test sequence" -- $ Given (print "Some effect") -- $ Given (print "Another effect") -- $ GivenAndAfter (print "Aquiring resource" >> return "Resource 1") -- (print . ("Release "++)) -- $ GivenAndAfter (print "Aquiring resource" >> return "Resource 2") -- (print . ("Release "++)) -- $ When (print "Action returning" >> return ([1..10]++[100..106]) :: IO [Int]) -- $ Then (@?= ([1..10]++[700..706])) -- $ End -- @ module Test.BDD.Language ( Language (..) , BDDPreparing , BDDTesting , BDDTest (..) , TestContext (..) , context , when , tests , interpret , Phase (..) ) where import Lens.Micro import Lens.Micro.TH -- | Separating the 2 phases by type data Phase = Preparing | Testing -- | Recording given actions and type related teardowns data TestContext m = forall r. TestContext (m r) (r -> m ()) -- | Bare hoare language data Language m t q a where -- | action to prepare the test Given :: m () -> Language m t q 'Preparing -> Language m t q 'Preparing -- | action to prepare the test, and related teardown action GivenAndAfter :: m r -> (r -> m ()) -> Language m t q 'Preparing -> Language m t q 'Preparing -- | core logic of the test (last preparing action) When :: m t -> Language m t q 'Testing -> Language m t q 'Preparing -- | action producing a test Then :: (t -> m q) -> Language m t q 'Testing -> Language m t q 'Testing -- | final placeholder End :: Language m t q 'Testing -- | Result of this module interpreter data BDDTest m t q = BDDTest { -- | tests from 't' _tests :: [t -> m q] , -- | test context _context :: [TestContext m] , -- | when action to compute 't' _when :: m t } makeLenses ''BDDTest -- | Preparing language types type BDDPreparing m t q = Language m t q 'Preparing -- | Testing language types type BDDTesting m t q = Language m t q 'Testing -- | An interpreter collecting the actions interpret :: Monad m => Language m t q a -> BDDTest m t q interpret (Given given p) = interpret $ GivenAndAfter given (const $ return ()) p interpret (GivenAndAfter given after p) = over context ((:) $ TestContext given after) $ interpret p interpret (When fa p) = set when fa $ interpret p interpret (Then ca p) = over tests ((:) ca) $ interpret p interpret End = BDDTest [] [] $ error "End on its own does not make sense as a test"