{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeOperators #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {-# LANGUAGE TemplateHaskell #-} module SpecHelper ( module X , module SpecHelper ) where ------------------------------------------------------------------------------- import Control.Applicative as X import Data.Attoparsec.Text as X (Parser, parseOnly) import qualified Data.List.NonEmpty as NE import Data.Maybe as X import Data.Monoid as X import Data.Text (Text) import Data.Time.Calendar as X (fromGregorian, toGregorian) import Data.Time.Clock as X (DiffTime, UTCTime (..), addUTCTime, secondsToDiffTime) import qualified Data.Time.Clock.POSIX as POSIX import Data.Time.LocalTime as X import Debug.Trace as X import GHC.Generics (Generic) import qualified GHC.Generics as G import Hedgehog import qualified Hedgehog.Gen as Gen import qualified Hedgehog.Range as Range import Test.Tasty as X import Test.Tasty.Hedgehog as X import Test.Tasty.HUnit as X ------------------------------------------------------------------------------- import System.Cron as X ------------------------------------------------------------------------------- -- | A necessary evil for generators that can generically make sure -- all constructors of your sum type are covered. The tradeoff is that -- you have no control over the range at the per-test level, so use -- this sparingly where full coverage is more valuable than -- fine-tuning generators. class HasGen a where gen :: Gen a default gen :: (Generic a, GHasGen (G.Rep a)) => Gen a gen = Gen.choice (fmap G.to <$> ggen) class GHasGen f where ggen :: [Gen (f a)] instance GHasGen G.U1 where ggen = [pure G.U1] instance (HasGen a) => GHasGen (G.K1 i a) where ggen = [G.K1 <$> gen] instance (GHasGen a, GHasGen b) => GHasGen (a G.:*: b) where ggen = [ (G.:*:) <$> f <*> g | f <- ggen, g <- ggen] instance (GHasGen a, GHasGen b) => GHasGen (a G.:+: b) where ggen = (fmap G.L1 <$> ggen) <> (fmap G.R1 <$> ggen) -- we don't care about metadata, lift over it instance (GHasGen c) => GHasGen (G.M1 _a _b c) where ggen = fmap G.M1 <$> ggen instance HasGen BaseField instance HasGen SpecificField instance HasGen RangeField instance HasGen Int where gen = Gen.int Range.linearBounded instance HasGen Text where gen = genAlpha instance HasGen CronField instance HasGen a => HasGen (NE.NonEmpty a) where gen = Gen.nonEmpty (Range.linear 1 50) gen instance HasGen a => HasGen [a] where gen = Gen.list (Range.linear 0 50) gen instance HasGen StepField where gen = Gen.just (mkStepField <$> gen <*> gen) instance HasGen CronSchedule instance HasGen Crontab instance HasGen CronCommand where gen = CronCommand <$> genAlpha instance HasGen CrontabEntry instance HasGen MinuteSpec where gen = Gen.just (mkMinuteSpec <$> gen) instance HasGen HourSpec where gen = Gen.just (mkHourSpec <$> gen) instance HasGen DayOfMonthSpec where gen = Gen.just (mkDayOfMonthSpec <$> gen) instance HasGen DayOfWeekSpec where gen = Gen.just (mkDayOfWeekSpec <$> gen) instance HasGen MonthSpec where gen = Gen.just (mkMonthSpec <$> gen) instance HasGen UTCTime where gen = genUTCTime genAlpha :: Gen Text genAlpha = Gen.text (Range.linear 1 50) Gen.alpha genUTCTime :: Gen UTCTime genUTCTime = genUTCTime' (Range.linear 0 maxBound) -- | genUTCTime with a range of posix seconds genUTCTime' :: Range.Range Int -> Gen UTCTime genUTCTime' = fmap POSIX.posixSecondsToUTCTime . genPOSIXTime genPOSIXTime :: Range.Range Int -> Gen POSIX.POSIXTime genPOSIXTime rnge = do fromInteger . toInteger <$> Gen.int rnge mkMinuteSpec' :: CronField -> MinuteSpec mkMinuteSpec' = fromJust . mkMinuteSpec mkHourSpec' :: CronField -> HourSpec mkHourSpec' = fromJust . mkHourSpec mkDayOfMonthSpec' :: CronField -> DayOfMonthSpec mkDayOfMonthSpec' = fromJust . mkDayOfMonthSpec mkMonthSpec' :: CronField -> MonthSpec mkMonthSpec' = fromJust . mkMonthSpec mkDayOfWeekSpec' :: CronField -> DayOfWeekSpec mkDayOfWeekSpec' = fromJust . mkDayOfWeekSpec mkRangeField' :: Int -> Int -> RangeField mkRangeField' a = fromJust . mkRangeField a mkSpecificField' :: Int -> SpecificField mkSpecificField' = fromJust . mkSpecificField mkStepField' :: BaseField -> Int -> StepField mkStepField' a = fromJust . mkStepField a ------------------------------------------------------------------------------- isLeft :: Either a b -> Bool isLeft (Left _) = True isLeft _ = False