packages feed

exitcode-0.1.0.6: test/Tests.hs

import           Control.Lens              (review, (^.), (^?))
import           Control.Monad.Trans.Maybe (MaybeT (MaybeT))
import           Data.Functor.Classes      (Eq1)
import           Data.Functor.Identity     (Identity (..), runIdentity)
import           Data.Monoid               ((<>))

import           Hedgehog                  (forAll, property, (===))
import qualified Hedgehog.Gen              as Gen
import           Hedgehog.Internal.Gen     (MonadGen)
import qualified Hedgehog.Range            as Range
import           Test.QuickCheck           (Arbitrary)
import           Test.QuickCheck.Checkers  (EqProp (..), Test, TestBatch, eq)
import           Test.QuickCheck.Classes   (applicative, functor)
import           Test.Tasty                (TestTree, defaultMain, testGroup)
import           Test.Tasty.Hedgehog       (testProperty)
import           Test.Tasty.HUnit          (testCase, (@?=))
import qualified Test.Tasty.QuickCheck     as TQC

import           Control.Exitcode          (Exitcode, ExitcodeT, exitCode,
                                            exitfailure0, exitsuccess,
                                            exitsuccess0, runExitcode,
                                            runExitcodeT, _ExitFailure,
                                            _ExitSuccess, ExitcodeT0)

import           System.Exit               (ExitCode (..))

newtype EW f e a = EW { unEW :: ExitcodeT f e a } deriving (Eq, Show)

instance (Monad f, Arbitrary e, Arbitrary a) => Arbitrary (EW f e a) where
  arbitrary = fmap (EW . pure) TQC.arbitrary

instance Functor f => Functor (EW f e) where
  fmap f = EW . fmap f . unEW

instance Monad f => Applicative (EW f e) where
  pure = EW . pure
  EW f <*> EW a = EW (f <*> a)

instance (Eq1 f, Eq e, Eq a) => EqProp (EW f e a) where
  (=-=) = eq

type CheckMe = EW [] String (Integer, Integer, Integer)

nonZero :: MonadGen m => m Int
nonZero =
  let allOfTheInts = Range.linear (minBound :: Int) (maxBound :: Int)
   in Gen.filter (/= 0) (Gen.int allOfTheInts)

main :: IO ()
main = defaultMain test_Exitcode

test_Exitcode :: TestTree
test_Exitcode =
  testGroup "Exitcode" [
    tastyCheckersBatch $ functor (undefined :: CheckMe)
  , tastyCheckersBatch $ applicative (undefined :: CheckMe)
  , applicativeTest
  , exitFailureTraversalTest
  , exitSuccessPrismTest
  , exitfailure0Test
  , exitCodePrismTest
  ]

applicativeTest :: TestTree
applicativeTest =
  testGroup "Applicative" [
    testCase "Sticks to the Right" $
      pure (<> "bar") <*> pure "foo" @?= (exitsuccess "foobar" :: Exitcode String String)
  ]

exitFailureTraversalTest :: TestTree
exitFailureTraversalTest =
  testGroup "_ExitFailure Traversal" [
    testProperty "view non-zero input" . property $
      forAll nonZero >>= (\n -> (exitfailure0 n :: ExitcodeT0 Identity) ^? _ExitFailure === Just ((), n))
  , testCase "view 0" $
      (exitfailure0 0 :: ExitcodeT0 Identity) ^? _ExitFailure @?= Nothing
  ]

exitSuccessPrismTest :: TestTree
exitSuccessPrismTest =
  testGroup "_ExitSuccess Prism" [
    testCase "review" $
      review _ExitSuccess () @?= (exitsuccess0 :: ExitcodeT Identity () ())
  , testCase "view exitsuccess0" $
      (exitsuccess0 :: ExitcodeT Identity () ()) ^? _ExitSuccess @?= Just ()
  , testProperty "view exitfailure0 non-zero" . property $
      forAll nonZero >>= (\n -> exitfailure0 n ^? _ExitSuccess === Nothing)
  , testCase "view exitfailure0 0" $
      exitfailure0 0 ^? _ExitSuccess @?= Just ()
  ]

exitfailure0Test :: TestTree
exitfailure0Test =
  testGroup "exitfailure0" [
    testProperty "non-zero input" . property $
      forAll nonZero >>= (\n ->
        runExitcode (exitfailure0 n) === Left ((), n))
  , testCase "0" $
      runExitcode (exitfailure0 0) @?= Right ()
  ]

exitCodePrismTest :: TestTree
exitCodePrismTest =
  testGroup "exitCode Prism" [
    testProperty "`exitfailure0 n`, where n is non-zero" . property $
      forAll nonZero >>= \n -> (review exitCode (exitfailure0 n)) === Identity (ExitFailure n)
  , testCase "review `exitfailure 0`" $
      runIdentity (review exitCode (exitfailure0 0)) @?= ExitSuccess
  , testCase "review `exitsuccess0`" $
      runIdentity (review exitCode exitsuccess0) @?= ExitSuccess
  , testProperty "view ExitFailure n, where n is non-zero" . property $
      forAll nonZero >>= (\n -> Identity (ExitFailure n) ^? exitCode === Just (exitfailure0 n))
  , testCase "view ExitFailure 0" $
      let _ = ""
      in  runExitcodeT (Identity (ExitFailure 0) ^. exitCode) @?= (MaybeT (Identity Nothing))
  , testCase "view ExitSuccess" $
      Identity ExitSuccess ^? exitCode @?= Just exitsuccess0
  ]

tastyCheckersBatch :: TestBatch -> TestTree
tastyCheckersBatch (name, tests) =
  testGroup (name <> " laws") (tastyCheckersProperty <$> tests)

tastyCheckersProperty :: Test -> TestTree
tastyCheckersProperty =
  uncurry TQC.testProperty